How to create a L2 tunnel on Ubuntu

Let’s assu me have a network of 192.168.0.0/24 and we want it to span to another location separated by public internet.
The scheme would look like this:

192.168.0.0/24 - <eth1 ubuntu1 eth0> -public internet- <eth0 ubuntu1 eth1> - 192.168.0.0/24

and we want hosts in left part of this disconiguous network are able to communicate with hosts in right part.

1) Fot it to work we need both eth0 interface on ubuntu1 and ubuntu2 to have routable IP addresses. Let’s assume eth0 on ubuntu1 has 1.1.1.1 and eth0 on ubuntu2 has 2.2.2.2.

2) Load l2tp_eth module

modprobe l2tp_eth

For this module to be loaded at start, edit /etc/modules and add  l2tp_eth at the end of the file.

3) Set up a l2tp tunnel and session on both machines:

root@ubuntu1:~# ip l2tp add tunnel tunnel_id 1000 peer_tunnel_id 2000 encap udp local 1.1.1.1 remote 2.2.2.2 udp_sport 6000 udp_dport 5000
root@ubuntu1:~# ip l2tp add session tunnel_id 1000 session_id 3000 peer_session_id 4000

root@ubuntu2:~# ip l2tp add tunnel tunnel_id 2000 peer_tunnel_id 1000 encap udp local 2.2.2.2 remote 1.1.1.1 udp_sport 5000 udp_dport 6000
root@ubuntu2:~# ip l2tp add session tunnel_id 2000 session_id 4000 peer_session_id 3000

To make this setting persistent create a script named l2tp_eth.sh for example, that would contain the configuration above (specific to each host)
and in /etc/network/interface  create a post-up job for eth0

auto eth0
iface eth0 inet static
        address 1.1.1.1
        netmask 255.255.255.0
        network 1.1.1.0
        broadcast 1.1.1.255
        gateway 1.1.1.2
        dns-nameservers 1.1.1.2 8.8.8.8
        post-up /path/to/scipt/l2tp_eth.sh

Don’t forget to make the script executable with

chmod +x /path/to/scipt/l2tp_eth.sh

4) Now you have a l2tpeth0 interface (type ip address show to be sure). It’s time to set up a bridge between internal eth1 and l2tpeth0.
This is how it should look like in /etc/network/interface

auto l2tp_br
iface l2tp_br inet static
        address 192.168.0.11
        netmask 255.255.255.0
        network 192.168.0.0
        broadcast192.168.0.255
        bridge_ports l2tpeth0 eth1
        bridge_stp off
        bridge_fd 0
        bridge_maxwait 0

I picked 192.168.0.11 as this bridge interface for ubuntu1 and 192.168.0.22 for ubuntu2. If this addess doesn’t shows up after reboot, probably you are missing bridge-utils, that can be installed with apt-getinstall bridge-utilsThese addresses can be used as a GWs for some servers in networks left part to access internet hosts provided NAT is set up with

iptables -t nat -A POSTROUTING -o eth0 -s 192.168.0.0/24 -j MASQUERADE

If you are using virtualized servers with VMware vsphere is’t necessary to allow Promiscuous Mode on vswitch or port group so that eth1 would accept frames destined to other hosts.

5) As L2TP is encapsulated into IP and it itself encapsulates ethernet frames we should tune MTU for packets inside the network.

root@ubuntu1:~# ip link set eth1 mtu 1446
root@ubuntu2:~# ip link set eth1 mtu 1446

That’s it. You can place a server in either part of 192.168.0.0/24 network and they will be able to communicate with each other. I suppose these scheme can be used to turn CWMS HA installation into MDC without buying a license.

Recording storage server for CWMS

WebEx Meeting Server allows you to save recordings of your meeting. For it to work there should be NFS share available. I’m using openfiler for that.

1) Get openfiler ISO from https://www.openfiler.com/community/download

2) Create a VM (Other Linux 64-bit) on ESXi host with 2 hard disks: one for the system itself (10GB will be enough), second for storage (I start with 500GB).

3) Boot your VM from ISO and choose graphical installation

4) When it comes to selecting drives, deselect sdb – we will create partitions on it later
500-1

5) When installation is over goto https://<openfiler_IP&gt;:446 and login with openfiler/password credentials

6) Go to Accounts -> Admin password and change to something more secure

7) Start NFS (Services tab)
500-2

8) Set up network access (System tab). I added all network with CWMS servers.
500-3
9) Create volumes. Navigate to Volumes tab and hit “create new physical volumes”, then select /dev/sdb disk
500-4

10) Create a “Physical volume” partition
500-5

11) Click Add volumes (right menu) and create volume group
500-6
12)  Click Add volumes (right menu) again and create volume. Choose XFS Filesystem / Volume type:
500-7
13) Volume is not a share. Go to Shares tab, click on a volume (nfs_for_webex) and add a folder. Then click on e newly added folder and hit “Make share”.
500-8

14) Select public access under “Share Access Control Mode”, hit Update and give RW rights to access group (network from step 8) and hit Update there too
500-9

15) Go to CWMS administration interface and put the whole cluster to Maintenance mode. Then navigate to System -> Servers -> Add storage server and enter <openfiler_IP>:/mnt/vg_nfs/vol_nfs/wx_data/ under NFS mount point field. After a minute or so you’ll see a green sign (Status: Server UP) indicating that storage was added.

16) Go to settings -> Meetings and check Record checkbox. Not attendies can record their meetings (not only audio and video, but also presentaions and screen captures). You’ll also would need special player for these recording. The player installation is available from CWMS user interface.

How to start a Web-server with nginx+uwsgi+flask

The fastest way of providing a web interface to your Python project is, in my opinion, to use Flask (http://flask.pocoo.org/). But it’s built-in web server is good only for testing and development (in my case I’ve got some strange errors like broken pipes when I tried to put in production under moderate load). So the recommended option is to use nginx and uwsgi as a web server and flash as a backend.
1) In case of a fresh installation of Ubunty 14.04 first thing you should do is to install some packages: pip for installation of usefull Python modules, python-dev in order to enable pip to install uwsgi and nginx

sudo apt-get update
sudo apt-get install python-pip python-dev nginx

2)    Let’s also install virtualenv. Ubunty Trusty comes with python 2.7.6 which lacks ssl library. My previous idea was to install python 2.7.9 into separate directory and to use it, but later I found cool molude named requests (http://docs.python-requests.org/en/latest/) which deal with all ssl stuff, so now I’d rather use systemwide python. But in order to isolate it from other admins who like to mess with python modules it’s better to use  virtualenv

sudo pip install virtualenv

3)    Now let’s create our projects directories

sudo mkdir /opt/my_flask
cd /opt/my_flask

4)    We will store our virtualized python here with a command

sudo virtualenv my_flask_env

5)    Now we have to activate it

sudo source my_flask_env /bin/activate

And the prompt would look like:

(my_flask_env)user@host:/opt my_flask$

6)    Install flask and uwsgi

sudo pip install uwsgi flask

7)    Create a sample application with flask

sudo vi /opt/my_flask/my_flask.py

Here we need to import Flask module and create a route of / which will return HTML code when requested

from flask import Flask
application = Flask(__name__)

@application.route("/")
def hello():
    return "<span>Hello world!</span>"

if __name__ == "__main__":
    application.run(host='0.0.0.0')

8)    Now we have to create uwsgi entry point, which will link uwsgi with our flaks application

sudo vi /opt/my_flask/wsgi.py
from my_flask import application

if __name__ == "__main__":
    application.run()

9)    It’s time to make a configuration file for uwsgi

sudo vi /opt/my_flask/my_flask.ini
[uwsgi]
socket = /tmp/uwsgi.sock
module = my_flask
callable = application
enable-threads = true
chmod-socket = 666
vacuum = true
die-on-term = true
req-logger = file:/tmp/reqlog
logger = file:/tmp/errlog

/tmp/uwsgi.sock – is a special socket used as a pipe between nginx and uwsgi process and uses special uwsgi protocol.

die-on-term – will help to start and stop our process with init scripts,

vacuum is needed for socket clean up

10)    Now let’s create an upstart script and a separate user

sudo useradd --no-create-home nginx
sudo vi /etc/init/my_flask.conf
description "uWSGI server of my_flask"
start on runlevel [2345]
stop on runlevel [!2345]
setuid nginx
setgid www-data
env PATH=/opt/my_flask/my_flask_env/bin
chdir /opt/my_flask
exec uwsgi --ini my_flask.ini

11)    Starting uwsgi is simple – just type

sudo start myproject

12)    Now let’s configure nginx to connect to the socket

sudo vi /etc/nginx/sites-available/default

And inside server block paste this

server {
    listen 80;
    server_name server_domain_or_IP;

    location / {
        include uwsgi_params;
        uwsgi_pass unix=:/tmp/uwsgi.sock
    }
}

13)Restart nginx with

sudo service nginx restart

And that’s it. Navigate to http://server_domain_or_IP and I’ll be able to see Hello world!
You can aslo find all logs in /tmp/reqlog and /tmp/errlog

Video broadcasting and recording with Cisco part 5. flowplayer

Now let’s install web-server which will serve a web-page with flowplayer which will display our live broadcast. We will instruct our users to open link like player.example.com/?stream=1002 in order to start browsing the broadcast.

1) Let’s install Apache and WSGI

# sudo apt-get update
# sudo apt-get upgrade
# sudo apt-get install apache2
# sudo apt-get install libapache2-mod-wsgi

2) Install Flask framework

# wget https://bootstrap.pypa.io/get-pip.py
# python get-pip.py
# python -m pip install flask

3) Inside /var/www/html create wsgi and put player.wsgi inside

#player.wsgi
 import sys
 sys.path.append('/var/www/html/wsgi')
 from player import app as application

4) Inside wsgi folder create player.py

# -*- coding: utf-8 -*-
from flask import Flask, jsonify, render_template, request, redirect, url_for
app = Flask(__name__)
import httplib
import urllib2
import xml.etree.ElementTree as ET


def get_nclients(host):
    try:
        passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
        passman.add_password(None, 'http://'+host+':8080', '', '')
        authhandler = urllib2.HTTPDigestAuthHandler(passman)
        opener = urllib2.build_opener(authhandler)
        res = opener.open('http://'+host+':8080/stat',timeout=2)
        response = res.read()
        res.close()
        summ=0
        tree = ET.fromstring(response)        
        serv=tree.find('server')
        #print serv[0][0].text
        #print serv[1][0].text
        for app in serv:
            for stream in app[1]:
                for clients in stream.findall('nclients'):
                    summ=summ+int(clients.text)
        return summ    
    except:
        return 999

def streams(host):
    result=[]
    try:
        url = 'http://'+host+':8086/connectioncounts'
        mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
        mgr.add_password(None,url,'***','***')
        opener = urllib2.build_opener(urllib2.HTTPBasicAuthHandler(mgr), urllib2.HTTPDigestAuthHandler(mgr))
        urllib2.install_opener(opener)
        f = urllib2.urlopen(url,timeout=2)
        tree = ET.fromstring(f.read())
        vhost = tree.find('VHost')
        for stream in vhost.iter('Stream'):
            result.append(stream.find('Name').text)
    except:
        result=[]
    return result

@app.route("/")
def hello():
    stream=request.args.get('stream')
    rtlrs=['192.168.10.1','192.168.11.1','192.168.12.1']
    rtlr_found=False
    while not rtlr_found:
               temp=[]
               for i in rtlrs:           
                    nclients=get_nclients(i)
                    if nclients

Logic behind this script is following.

a) When users visit our url first of all we get stream number they are looking for with stream=request.args.get(‘stream’) inside hello function

b) Assume we have a number of nginx retranslators [‘192.168.10.1′,’192.168.11.1′,’192.168.12.1’]. Let’s query them for a number of connected users and choose least loaded one. get_nclients function will help us with it. If there are more that 700 users connected to a certain retranslator we won’t use it.

c) We also need to be sure that stream is present as Wowza server, so let’s query it for all it streams with streams function. If there is no stream let’s inform users about it. Do not forget to change Wowza admin passwords inside this function.

d) Finally generate index.html and pass url for a stream to it.

5) Index.html will look like that (don’t forget to delete hashtags in the beginning of every line):

#<!DOCCTYPE html>
#<html><head>
#<meta http-equiv="content-type" content="text/html; charset=UTF-8">
#<meta charset="utf-8">
#<head>
# <link rel="stylesheet" href="//releases.flowplayer.org/6.0.1/skin/minimalist.css">
# //code.jquery.com/jquery-1.11.2.min.js
# //releases.flowplayer.org/6.0.1/flowplayer.min.js
#</head>
#<title>Player</title>
#</head>
#<body>
#
#toggle video #
# # # #
#</div> #</body> #</html>

6) And errors.html like that (don’t forget to delete hashtags in the beginning of every line):

#<!DOCCTYPE html>
#<html><head>
#<meta http-equiv="content-type" content="text/html; charset=UTF-8">
#<meta charset="utf-8">
#<title>Player</title>
#</head>
#<body>
#<span id="debug">{{error}} </span>
#</body>
#</html>

7) Final step is to edit virtual hosts in Apache config at /etc/apache2/sites-enabled/000-default.conf (don’t forget to delete hashtags in the beginning of every line):

     #<VirtualHost *>
     #ServerName 
     #DocumentRoot /var/www/html
     #WSGIDaemonProcess player
     #WSGIScriptAlias / /var/www/html/wsgi/player.wsgi
     #<Directory /var/www/wsgi>
         WSGIProcessGroup player
         WSGIApplicationGroup %{GLOBAL}
         Order deny,allow
          Allow from all
     #</Directory>

8) Restart Apache with service apache2 restart and you are ready to go. In case of ploblems with flowplayer consult http://demos.flowplayer.org/

Video broadcasting and recording with Cisco part 4. Wowza+Nginx

During these series we have installed Cisco Telepresence Content Server and Wowza Media Streaming Engine. TCS works as SIP gateway and converts SIP signalling with RTP payload to RTSP signalling with RTP for live streams. Wowza accepts RTSP and converts it to RTMP which can be used with Flash players and HLS for HTML5 (we will discuss HLS later).
Imagine that you have to provide live streaming for more than 1000 users. In that case Wowza have a solution – Wowza Origin and Wowza Edge. The idea behind it that you have one generating Wowza server and a number of retranslators (edges), which are situated closer to users. That’s great but you have to buy license for all those edges and it summs up to a big money. Maybe it’s worth investing when you have big video broadcasting events every week, but what if your weekly translations cover 700 users and such big events are held once a year. In that case there is open-source solution for retranslators: nginx web server with nginx-rtmp plugin (https://github.com/arut/nginx-rtmp-module).

1) Assume we have fresh installation of Ubuntu 14.04. Let’s install all supplementary packages.

# apt-get update
# apt-get upgrade
# apt-get install build-essential libpcre3 libpcre3-dev libpcrecpp0 libssl-dev zlib1g-dev git checkinstall

2) Create separate user for nginx

# useradd --no-create-home nginx

3) Download nginx and rtmp plugin sources

# mkdir /usr/build 
# cd /usr/build 
# git clone git://github.com/arut/nginx-rtmp-module.git 
# wget http://nginx.org/download/nginx-1.7.11.tar.gz

4) Unpack, configure, compile and install package. Again I’m using checkinstall to get deb packet and the ability to delete installed package with dpkg in future

# tar zxfv nginx-1.7.11.tar.gz
# cd nginx-1.7.11
# ./configure --prefix=/usr/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/lock/nginx.lock --with-http_ssl_module --user=nginx --group=nginx --with-http_stub_status_module --with-http_gzip_static_module --add-module=/usr/build/nginx-rtmp-module
# make
# checkinstall --pkgname nginx-rtmp

5) Create a script to launch nginx

# cd /etc/init.d
# vi nginx

Copy following to vi

# Description: starts nginx using start-stop-daemon
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/nginx/sbin:/usr/nginx/bin
DAEMON=/usr/nginx/sbin/nginx
NAME=nginx
DESC=nginx
test -x $DAEMON || exit 0
# Include nginx defaults if available
if [ -f /etc/default/nginx ] ; then
. /etc/default/nginx
fi
set -e
. /lib/lsb/init-functions
case "$1" in
start)
echo -n "Starting $DESC: "
start-stop-daemon --start --quiet --pidfile /var/run/$NAME.pid \
--exec $DAEMON -- $DAEMON_OPTS || true
echo "$NAME."
;;
stop)
echo -n "Stopping $DESC: "
start-stop-daemon --stop --quiet --pidfile /var/run/$NAME.pid \
--exec $DAEMON || true
echo "$NAME."
;;
restart|force-reload)
echo -n "Restarting $DESC: "
start-stop-daemon --stop --quiet --pidfile \
/var/run/$NAME.pid --exec $DAEMON || true
sleep 1
start-stop-daemon --start --quiet --pidfile \
/var/run/$NAME.pid --exec $DAEMON -- $DAEMON_OPTS || true
echo "$NAME."
;;
reload)
echo -n "Reloading $DESC configuration: "
start-stop-daemon --stop --signal HUP --quiet --pidfile /var/run/$NAME.pid \
--exec $DAEMON || true
echo "$NAME."
;;
status)
status_of_proc -p /var/run/$NAME.pid "$DAEMON" nginx && exit 0 || exit $?
;;
*)
N=/etc/init.d/$NAME
echo "Usage: $N {start|stop|restart|reload|force-reload|status}" >&2
exit 1
;;
esac
exit 0

5) Make it executable and add to autolaunch. Now you can start, stop and restart it with

# /etc/init.d/nginx start|stop|restart command
# chmod +x /etc/init.d/nginx
# update-rc.d -f nginx defaults

6) Now post following to /etc/nginx/nginx.conf and restart nginx

worker_processes  1;
error_log /var/log/nginx/error.log  debug;
events {
worker_connections 1024;
}
rtmp {
    server {
        listen 1935;
        application live-edge {
                live on;
                pull rtmp://<wowza IP adress>:1935 app=live;
       }
    }
}

http {
        server {
                listen 8080;
                location /stat {
                        rtmp_stat all;
                        rtmp_stat_stylesheet stat.xsl;
                }
                location /stat.xsl {
                        root /usr/build/nginx-rtmp-module/;
                }
               location /control {
                        rtmp_control all;
                }
        }
}

RTMP section of the config creates RTMP server that serves live-egde RTMP application. Whenever user opens browser with flash player, the player makes a connection to this nginx server and provides app name (live-edge) and stream number (1002). When nginx gets this request it connects to Wowza and query for app=live and same stream name. When second user comes it servres him chached data without creating additional connection to Wowza.  You can as many such retranslators as you want.

Additionally you can query nginx for connected users by consulting http://<server IP>:8080/stat.

Installation of Python 2.7.9 in Ubuntu 14.04

Ubuntu 14.04 ships with python 2.7.6 and uses it a lot for it’s internal needs. But python2.7.6 lacks very usefull module – ssl (https://docs.python.org/2.7/library/ssl.html). I like ssl.create_default_context a lot – saves a lot of work when dealing with HTTPS. So here are the steps to get separate installation of python2.7.9 leaving default 2.7.6 intact 1) You have clean Ubuntu 14.04 and now it’s time to install some important packages

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install build-essential
sudo apt-get install libreadline-gplv2-dev libncursesw5-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev
sudo apt-get install checkinstall

2) Now let’s fetch python’s source and extract it to our home directory

mkdir ~/Downloads
cd ~/Downloads
wget http://python.org/ftp/python/2.7.9/Python-2.7.9.tgz
tar -xvf Python-2.7.9.tgz
cd Python-2.7.9

3) It’s time to compile to to a separate directory (say /opt/python-2.7.9)

sudo mkdir /opt/python279
./configure --prefix=/opt/python279
make

4) Here is an important step – we are not going to make install it, but use checkinstall instead. It will create deb package and install it (so it can be easily removed later if needed, for example). We will provide custom package (python-2.7.9) name to identify it later.

sudo checkinstall --pkgname python-2.7.9

5) That’s it. Type /opt/python279/bin/python -V and it will show the version 6) You can delete it later with sudo dpkg -r python-2.7.9