Revision cf51ea5b

b/README
32 32
Single bridge setup. Private IPs. Masquerade: 
33 33

  
34 34
For security and not being able to change ip-mac-tap key: 
35
# ebtables -t filter -D INPUT -i tap0 -j TAP0 
36
# ebtables -t filter -D FORWARD -i tap0 -j TAP0
37
# ebtables -t filter -X TAP0
38
# ebtables -t filter -N TAP0 
39
# ebtables -t filter -A TAP0 --ip-source \! 192.168.100.2 -p ipv4 -j DROP
40
# ebtables -t filter -A TAP0 -s \! aa:00:00:8c:d3:a4 -j DROP 
41
# ebtables -t filter -A INPUT -i tap0 -j TAP0 (for masquerading)
42
# ebtables -t filter -A FORWARD -i tap0 -j TAP0 (for private lans)
43

  
35
# ebtables -N FROMTAP0 
36
# ebtables -A FROMTAP0 --ip-source \! 192.168.100.2 -p ipv4 -j DROP
37
# ebtables -A FROMTAP0 -s \! aa:00:00:8c:d3:a4 -j DROP 
38
# ebtables -A INPUT -i tap0 -j FROMTAP0 (for masquerading)
39
# ebtables -A FORWARD -i tap0 -j FROMTAP0 (for private lans)
40
# ebtables -N TOTAP0
41
# ebtables -A FORWARD -o tap0 -j TOTAP0
42
# ebtables -A OUTPUT -o tap0 -j TOTAP0
43
# ebtables -A TOTAP0 -s 6e:10:e1:a0:c3:0f -j ACCEPT (from gateway)
44
# ebtables -A TOTAP0 -s \! aa:0:0:8c:d3:a4/ff:ff:ff:ff:0:0 -j DROP 
44 45

  
45 46

  
46 47
Private LANs: 
b/add-network
1
#!/bin/bash
2

  
3
DIR=/var/lib/snf-network
4
SUBNET=$1
5
GATEWAY=$2
6
TYPE=$3
7
NAME=$4
8
RT_TABLES=/etc/iproute2/rt_tables
9

  
10

  
11

  
12
if [ $# -ne 4 ]; then
13
  echo "$0 <subnet> <gateway> <private/public> <name>"
14
  exit 1
15
fi
16

  
17

  
18

  
19
cat > $DIR/networks/$NAME <<EOF
20
SUBNET=$SUBNET
21
GATEWAY=$GATEWAY
22
TYPE=$TYPE
23
EOF
24

  
25

  
26
IDX=$(ls $DIR/networks | wc -l)
27

  
28
# remove old entry
29
sed -i '/^'"$IDX"'\ / d' $RT_TABLES
30

  
31
echo "$IDX rt_$NAME" >> $RT_TABLES
32

  
33

  
b/add-nodegroup
1
#!/bin/bash
2

  
3
DIR=/var/lib/snf-network
4
NODES=$1
5
ROUTER=$2
6
IFACE=$3
7
VLAN=$4
8
VLANS=$5
9
NAME=$6
10

  
11

  
12
if [ $# -ne 6 ]; then
13
  echo "$0 <list_nodes> <router> <iface> <public_vlan> <list_of_private_vlans> <name>"
14
  echo "$0 'dev88 89' 'dev88' 'eth0' '101' '2990 2999' 'default'"
15
  exit 1
16
fi
17

  
18

  
19

  
20
cat > $DIR/nodegroups/$NAME <<EOF
21
ROUTER=$ROUTER
22
INTERFACE=$IFACE
23
PUBLIC_VLAN=$VLAN
24
PRIVATE_VLANS=$VLANS
25
EOF
26

  
27

  
b/conf/default/nfdhcpd
1
# Defaults for nfdhcpd initscript
2
# sourced by /etc/init.d/nfdhcpd
3
# installed at /etc/default/nfdhcpd by the maintainer scripts
4

  
5
#
6
# This is a POSIX shell fragment
7
#
8

  
9
RUN="yes"
10

  
11
# Additional options that are passed to the Daemon.
12
DAEMON_OPTS=""
b/conf/init.d/nfdhcpd
1
#!/bin/sh
2
#
3
# This is free software; you may redistribute it and/or modify
4
# it under the terms of the GNU General Public License as
5
# published by the Free Software Foundation; either version 2,
6
# or (at your option) any later version.
7
#
8
# This is distributed in the hope that it will be useful, but
9
# WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License with
14
# the Debian operating system, in /usr/share/common-licenses/GPL;  if
15
# not, write to the Free Software Foundation, Inc., 59 Temple Place,
16
# Suite 330, Boston, MA 02111-1307 USA
17
#
18
### BEGIN INIT INFO
19
# Provides:          nfdhcpd
20
# Required-Start:    $network $local_fs $remote_fs
21
# Required-Stop:     $remote_fs
22
# Should-Start:
23
# Should-Stop:
24
# Default-Start:     2 3 4 5
25
# Default-Stop:      0 1 6
26
# Short-Description: NFQueue DHCP/RA server
27
### END INIT INFO
28

  
29
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
30

  
31
DAEMON=/usr/sbin/nfdhcpd
32
NAME=nfdhcpd
33
DESC="NFQUEUE-based DHCP/RA server" 
34
LOGDIR=/var/log/nfdhcpd
35

  
36
PIDFILE=/var/run/$NAME.pid
37

  
38
test -x $DAEMON || exit 0
39

  
40
. /lib/lsb/init-functions
41

  
42
# Default options, these can be overriden by the information
43
# at /etc/default/$NAME
44
DAEMON_OPTS=""          # Additional options given to the server
45

  
46
DIETIME=2              # Time to wait for the server to die, in seconds
47
                        # If this value is set too low you might not
48
                        # let some servers to die gracefully and
49
                        # 'restart' will not work
50

  
51
STARTTIME=1             # Time to wait for the server to start, in seconds
52
                        # If this value is set each time the server is
53
                        # started (on start or restart) the script will
54
                        # stall to try to determine if it is running
55
                        # If it is not set and the server takes time
56
                        # to setup a pid file the log message might 
57
                        # be a false positive (says it did not start
58
                        # when it actually did)
59
                        
60
LOGFILE=$LOGDIR/$NAME.log  # Server logfile
61
#DAEMONUSER=nfdhcp   # Users to run the daemons as. If this value
62
                        # is set start-stop-daemon will chuid the server
63

  
64
# Include defaults if available
65
if [ -f /etc/default/$NAME ] ; then
66
	. /etc/default/$NAME
67
fi
68

  
69
# Use this if you want the user to explicitly set 'RUN' in
70
# /etc/default/
71
if [ "x$RUN" != "xyes" ] ; then
72
    log_failure_msg "$NAME disabled, please adjust the configuration to your needs "
73
    log_failure_msg "and then set RUN to 'yes' in /etc/default/$NAME to enable it."
74
    exit 1
75
fi
76

  
77
# Check that the user exists (if we set a user)
78
# Does the user exist?
79
set -e
80

  
81
running_pid() {
82
# Check if a given process pid's cmdline matches a given name
83
    pid=$1
84
    name=$2
85
    [ -z "$pid" ] && return 1
86
    [ ! -d /proc/$pid ] &&  return 1
87
    cmd=`cat /proc/$pid/cmdline | tr "\000" "\n"|head -n 1 |cut -d : -f 1`
88
    # Is this the expected server
89
    [ "$cmd" != "$name" ] &&  return 1
90
    return 0
91
}
92

  
93
running() {
94
# Check if the process is running looking at /proc
95
# (works for all users)
96

  
97
    # No pidfile, probably no daemon present
98
    [ ! -f "$PIDFILE" ] && return 1
99
    pid=`cat $PIDFILE`
100
    running_pid $pid python || return 1
101
    return 0
102
}
103

  
104
start_server() {
105
	start_daemon -p $PIDFILE $DAEMON $DAEMON_OPTS
106
	errcode=$?
107
	return $errcode
108
}
109

  
110
stop_server() {
111
	killproc -p $PIDFILE $DAEMON
112
	rrcode=$?
113
	return $errcode
114
}
115

  
116
reload_server() {
117
    [ ! -f "$PIDFILE" ] && return 1
118
    pid=pidofproc $PIDFILE # This is the daemon's pid
119
    # Send a SIGHUP
120
    kill -1 $pid
121
    return $?
122
}
123

  
124
force_stop() {
125
# Force the process to die killing it manually
126
	[ ! -e "$PIDFILE" ] && return
127
	if running ; then
128
		kill -15 $pid
129
	# Is it really dead?
130
		sleep "$DIETIME"s
131
		if running ; then
132
			kill -9 $pid
133
			sleep "$DIETIME"s
134
			if running ; then
135
				echo "Cannot kill $NAME (pid=$pid)!"
136
				exit 1
137
			fi
138
		fi
139
	fi
140
	rm -f $PIDFILE
141
}
142

  
143

  
144
case "$1" in
145
  start)
146
	log_daemon_msg "Starting $DESC " "$NAME"
147
        # Check if it's running first
148
        if running ;  then
149
            log_progress_msg "apparently already running"
150
            log_end_msg 0
151
            exit 0
152
        fi
153
        if start_server ; then
154
            # NOTE: Some servers might die some time after they start,
155
            # this code will detect this issue if STARTTIME is set
156
            # to a reasonable value
157
            [ -n "$STARTTIME" ] && sleep $STARTTIME # Wait some time 
158
            if  running ;  then
159
                # It's ok, the server started and is running
160
                log_end_msg 0
161
            else
162
                # It is not running after we did start
163
                log_end_msg 1
164
            fi
165
        else
166
            # Either we could not start it
167
            log_end_msg 1
168
        fi
169
	;;
170
  stop)
171
        log_daemon_msg "Stopping $DESC" "$NAME"
172
        if running ; then
173
            # Only stop the server if we see it running
174
			errcode=0
175
            stop_server || errcode=$?
176
            log_end_msg $errcode
177
        else
178
            # If it's not running don't do anything
179
            log_progress_msg "apparently not running"
180
            log_end_msg 0
181
            exit 0
182
        fi
183
        ;;
184
  force-stop)
185
        # First try to stop gracefully the program
186
        $0 stop
187
        if running; then
188
            # If it's still running try to kill it more forcefully
189
            log_daemon_msg "Stopping (force) $DESC" "$NAME"
190
			errcode=0
191
            force_stop || errcode=$?
192
            log_end_msg $errcode
193
        fi
194
	;;
195
  restart|force-reload)
196
        log_daemon_msg "Restarting $DESC" "$NAME"
197
		errcode=0
198
        stop_server || errcode=$?
199
        # Wait some sensible amount, some server need this
200
        [ -n "$DIETIME" ] && sleep $DIETIME
201
        start_server || errcode=$?
202
        [ -n "$STARTTIME" ] && sleep $STARTTIME
203
        running || errcode=$?
204
        log_end_msg $errcode
205
	;;
206
  status)
207

  
208
        log_daemon_msg "Checking status of $DESC" "$NAME"
209
        if running ;  then
210
            log_progress_msg "running"
211
            log_end_msg 0
212
        else
213
            log_progress_msg "apparently not running"
214
            log_end_msg 1
215
            exit 1
216
        fi
217
        ;;
218
  reload)
219
        log_warning_msg "Reloading $NAME daemon: not implemented, as the daemon"
220
        log_warning_msg "cannot re-read the config file (use restart)."
221
	;;
222
  *)
223
	N=/etc/init.d/$NAME
224
	echo "Usage: $N {start|stop|force-stop|restart|force-reload|status}" >&2
225
	exit 1
226
	;;
227
esac
228

  
229
exit 0
b/connect-network
1
#!/bin/bash
2

  
3
DIR=/var/lib/snf-network
4
NETWORK=$1
5
NODEGROUP=$2
6
MODE=$3
7
LINK=$4
8

  
9
source /etc/default/snf-network
10

  
11
if [ $# -ne 4 ]; then
12
  echo "$0 <network> <nodegroup> <mode> <link>"
13
  exit 1
14
fi
15

  
16
NETWORK_FILE=$DIR/networks/$NETWORK
17
NODEGROUP_FILE=$DIR/nodegoups/$NODEGROUP
18
INTERFACES=$DIR/interfaces/$NETWORK-$NODEGROUP
19

  
20
source $NETWORK_FILE
21
source $NODEGROUP_FILE
22

  
23
if [ $MODE == "routed" ]; then 
24
  VLAN=$LINK
25
  if [ $TYPE == "public" ]; then
26
    APR_IP=$(ipcalc $SUBNET | grep HostMax | awk '{print $2}')
27
    cat > $INTERFACES<<EOF
28
# $VLAN $MODE
29
auto $VLAN
30
iface $VLAN inet manual
31
#    ip-routing-table rt_$NETWORK
32
#    ip-routes $SUBNET
33
#    ip-gateway $GATEWAY
34
#    ip-forwarding 1
35
#    ip-proxy-arp 1
36
#    arp-ip $ARP_IP
37
EOF 
38
    ifup -i $INTERFACES $VLAN
39
    ip link set $VLAN up
40

  
41
    ip rule add iif $VLAN table rt_$NAME
42

  
43
    ip route add $SUBNET dev $VLAN table main 
44

  
45
    ip route add $SUBNET dev $VLAN table rt_$NAME
46
    ip route add default via $GATEWAY dev $VLAN table rt_$NAME
47
    
48
    echo 1 > /proc/sys/net/ipv4/conf/all/forwarding
49

  
50
    arptables -A OUTPUT -o $VLAN --opcode request -j mangle --mangle-ip-s  $ARP_IP 
51
  fi
52
fi
53

  
54

  
55

  
56
if [ $MODE == "bridged" ]; then
57
  BRIDGE=$LINK
58
  echo 1 > /proc/sys/net/ipv4/ip_forward
59
  if [ $TYPE == "public" ]; then
60
    VLAN=$INTERFACE.$PUBLIC_VLAN_ID
61
  elif [ $TYPE == "private" ]; then
62
    VLAN_ID=${PRIVATE_VLAN_IDS%% *}
63
    VLAN_IDS=${PRIVATE_VLAN_IDS#* }
64
    sed -i 's/PRIVATE_VLAN_IDS/ s/=.*/='"VLAN_IDS"'/' $NODEGROUP_FILE
65
    #set -- $PRIVATE_VLAN_IDS
66
    #VLAN=$1
67
    #shift
68
    #VLANS=$@
69
    VLAN=$INTERFACE.$VLAN_ID
70
  fi
71
  cat > $INTERFACES <<EOF
72
# $VLAN $MODE $BRIDGE
73
auto $VLAN
74
iface $VLAN inet manual
75

  
76
auto $BRIDGE
77
iface $BRIDGE inet manual
78
  bridge_ports $VLAN
79
  bridge_stp off
80
  bridge_fd 2
81
EOF
82
  ifup -i $INTERFACES $BRIDGE
83
  ip link set $VLAN up
84
  ip route add $SUBNET dev $BRIDGE table main
85

  
86
  ip route add $SUBNET dev $BRIDGE table rt_$NETWORK
87
  if [ ! -z $GATEWAY ]; then
88
    ip route add default via  dev $BRIDGE table rt_$NETWORK
89
    if [ $TYPE == "private" ]; then 
90
      if [ ! -z $ROUTER ]; then 
91
        if [ $(hostname) == $ROUTER ]; then
92
          NETMASK=$(ipcalc $SUBNET | grep Netmask | awk '{print $4}')
93
          ip addr add $GATEWAY/$NETMASK dev $BRIDGE
94
          iptables -t nat -A POSTROUTING -s $SUBNET \! -d $SUBNET -j MASQUERADE
95
        fi  
96
      fi
97
    fi
98
  fi
99
fi
b/disconnect-network
1
#!/bin/bash
2

  
3
DIR=/var/lib/snf-network
4
NETWORK=$1
5
NODEGROUP=$2
6

  
7
source /etc/default/snf-network
8

  
9
if [ $# -ne 2 ]; then
10
  echo "$0 <network> <nodegroup>"
11
  exit 1
12
fi
13

  
14
NETWORK_FILE=$DIR/networks/$NETWORK
15
NODEGROUP_FILE=$DIR/nodegoups/$NODEGROUP
16
INTERFACES=$DIR/interfaces/$NETWORK-$NODEGROUP
17

  
18
read x VLAN BRIDGE < $INTERFACES
19

  
20
VLAN_ID=${VLAN#*:}
21

  
22
source $NETWORK_FILE
23
source $NODEGROUP_FILE
24

  
25
if [ $MODE == "routed" ]; then 
26
  if [ $TYPE == "public" ]; then
27
    APR_IP=$(ipcalc $SUBNET | grep HostMax | awk '{print $2}')
28
    ip rule del iif $VLAN table rt_$NAME
29

  
30
    ip route del $SUBNET dev $VLAN table main 
31

  
32
    ip route del $SUBNET dev $VLAN table rt_$NAME
33
    ip route del default via $GATEWAY dev $VLAN table rt_$NAME
34

  
35
    arptables -D OUTPUT -o $VLAN --opcode request -j mangle --mangle-ip-s  $ARP_IP 
36
    ifdown -i $INTERFACES $VLAN
37
    rm $INTERFACES
38
  fi
39
fi
40

  
41

  
42

  
43
if [ $MODE == "bridged" ]; then
44
  if [ $TYPE == "private" ]; then
45
    VLAN_IDS="$VLAN_ID $PRIVATE_VLAN_IDS"
46
    sed -i 's/PRIVATE_VLAN_IDS/ s/=.*/='"VLAN_IDS"'/' $NODEGROUP_FILE
47
  fi
48

  
49
  ip route del $SUBNET dev $BRIDGE table main
50

  
51
  ip route del $SUBNET dev $BRIDGE table rt_$NETWORK
52
  if [ ! -z $GATEWAY ]; then
53
    ip route del default via $GATEWAY dev $BRIDGE table rt_$NETWORK
54
    if [ $TYPE == "private" ]; then 
55
      if [ ! -z $ROUTER ]; then 
56
        if [ $(hostname) == $ROUTER ]; then
57
          NETMASK=$(ipcalc $SUBNET | grep Netmask | awk '{print $4}')
58
          ip addr del $GATEWAY/$NETMASK dev $LINK 
59
          iptables -t nat -D POSTROUTING -s $SUBNET \! -d $SUBNET -j MASQUERADE
60
        fi  
61
      fi
62
    fi
63
  fi
64
  ifdown -i $INTERFACES $BRIDGE
65
  rm $INTERFACES
66
fi
b/interfaces
1
# IP-less inteface, used to route public IPv4
2
# for Synnefo VMs
3
auto eth0.101
4
iface eth0.101 inet manual
5
    ip-routing-table rt_public
6
    ip-routes 62.217.123.128/27
7
    ip-gateway 62.217.123.129
8
    ip-forwarding 1
9
    ip-proxy-arp 1
10
    arp-ip 62.217.123.158
11

  
12
#auto eth0.100
13
iface eth0.100 inet manual
14
  up ip link set eth0.100 up
15

  
16
#auto br100
17
iface br100 inet static
18
  # needed for being the rooter for the VMs
19
  address 192.168.100.1
20
  netmask 255.255.255.240
21
  bridge_ports eth0.100
22
  # needed by nfdhcpd to make DHCP responses
23
  up ip route add 192.168.100.0/28 dev br100 table rt_net100
24
  up ip route add default via 192.168.100.1 dev br100 table rt_net100
25
  # needed for the VMs to connect to the world
26
  up iptables -t nat -A POSTROUTING -s 192.168.100.0/28 \! -d 192.168.100.0/28 -j MASQUERADE
27
  down iptables -t nat -D POSTROUTING -s 192.168.100.0/28 \! -d 192.168.100.0/28 -j MASQUERADE
28
  bridge_stp off
29
  bridge_fd 2
30

  
31
#auto br100:1
32
iface br100:1 inet static
33
  # needed for being the rooter for the VMs
34
  address 192.168.101.1
35
  netmask 255.255.255.240
36
  up ip route add 192.168.101.0/28 dev br100 table rt_net101
37
  up ip route add default via 192.168.101.1 dev br100 table rt_net101
38
  # needed for the VMs to connect to the world
39
  up iptables -t nat -A POSTROUTING -s 192.168.101.0/28 \! -d 192.168.101.0/28 -j MASQUERADE
40
  down iptables -t nat -D POSTROUTING -s 192.168.101.0/28 \! -d 192.168.101.0/28 -j MASQUERADE
41

  
b/kvm-vif-bridge
1
#!/bin/bash
2

  
3
# This is an example of a Ganeti kvm ifup script that configures network
4
# interfaces based on the initial deployment of the Okeanos project
5

  
6
TAP_CONSTANT_MAC=cc:47:52:4e:45:54 # GRNET in hex :-)
7
MAC2EUI64=/usr/bin/mac2eui64
8
NFDHCPD_STATE_DIR=/var/lib/nfdhcpd
9

  
10
function routed_setup_ipv4 {
11
	# get the link's default gateway
12
	gw=$(ip route list table $TABLE | sed -n 's/default via \([^ ]\+\).*/\1/p' | head -1)
13

  
14
	# mangle ARPs to come from the gw's IP
15
	arptables -D OUTPUT -o $INTERFACE --opcode request -j mangle >/dev/null 2>&1
16
	arptables -A OUTPUT -o $INTERFACE --opcode request -j mangle --mangle-ip-s "$gw"
17

  
18
	# route interface to the proper routing table
19
	while ip rule del dev $INTERFACE; do :; done
20
	ip rule add dev $INTERFACE table $TABLE
21

  
22
	# static route mapping IP -> INTERFACE
23
	ip route replace $IP proto static dev $INTERFACE table $TABLE
24

  
25
	# Enable proxy ARP
26
	echo 1 > /proc/sys/net/ipv4/conf/$INTERFACE/proxy_arp
27
}
28

  
29
function routed_setup_ipv6 {
30
	# Add a routing entry for the eui-64
31
	prefix=$(ip -6 route list table $TABLE | awk '/\/64/ {print $1; exit}')
32
	uplink=$(ip -6 route list table $TABLE | sed -n 's/default via .* dev \([^ ]\+\).*/\1/p' | head -1)
33
	eui64=$($MAC2EUI64 $MAC $prefix)
34

  
35
	while ip -6 rule del dev $INTERFACE; do :; done
36
	ip -6 rule add dev $INTERFACE table $TABLE
37
	ip -6 ro replace $eui64/128 dev $INTERFACE table $TABLE
38
	ip -6 neigh add proxy $eui64 dev $uplink
39

  
40
	# disable proxy NDP since we're handling this on userspace
41
	# this should be the default, but better safe than sorry
42
	echo 0 > /proc/sys/net/ipv6/conf/$INTERFACE/proxy_ndp
43
}
44

  
45
# pick a firewall profile per NIC, based on tags (and apply it)
46
function routed_setup_firewall {
47
	ifprefix="synnefo:network:$INTERFACE_INDEX:"
48
	for tag in $TAGS; do
49
		case ${tag#$ifprefix} in
50
		protected)
51
			chain=protected
52
		;;
53
		unprotected)
54
			chain=unprotected
55
		;;
56
		limited)
57
			chain=limited
58
		;;
59
		esac
60
	done
61

  
62
	# Flush any old rules. We have to consider all chains, since
63
	# we are not sure the instance was on the same chain, or had the same
64
	# tap interface.
65
	for oldchain in protected unprotected limited; do
66
		iptables  -D FORWARD -o $INTERFACE -j $oldchain 2>/dev/null
67
		ip6tables -D FORWARD -o $INTERFACE -j $oldchain 2>/dev/null
68
	done
69

  
70
	if [ "x$chain" != "x" ]; then
71
		iptables  -A FORWARD -o $INTERFACE -j $chain
72
		ip6tables -A FORWARD -o $INTERFACE -j $chain
73
	fi
74
}
75

  
76
function routed_setup_nfdhcpd {
77
	umask 022
78
	cat >$NFDHCPD_STATE_DIR/$INTERFACE <<EOF
79
IFACE=$1
80
IP=$IP
81
MAC=$MAC
82
LINK=$TABLE
83
HOSTNAME=$INSTANCE
84
TAGS="$TAGS"
85
EOF
86
}
87

  
88
function make_ebtables {
89
  TAP=$INTERFACE
90
  FROM=FROM${TAP^^}
91
  TO=TO${TAP^^}
92
  
93
  ebtables -D INPUT -i $TAP -j $FROM
94
  ebtables -D FORWARD -i $TAP -j $FROM
95
  ebtables -D FORWARD -o $TAP -j $TO
96
  ebtables -D OUTPUT -o $TAP -j $TO
97
  
98
  ebtables -X $FROM
99
  ebtables -X $TO
100

  
101
  ebtables -N $FROM
102
  ebtables -A $FROM --ip-source \! $IP -p ipv4 -j DROP
103
  ebtables -A $FROM -s \! $MAC -j DROP 
104
  ebtables -A INPUT -i $TAP -j $FROM 
105
  ebtables -A FORWARD -i $TAP -j $FROM 
106
  ebtables -N $TO
107
  ebtables -A FORWARD -o $TAP -j $TO
108
  ebtables -A OUTPUT -o $TAP -j $TO
109
  if [ $TYPE == "private" ]; then 
110
    ebtables -A $TO -s \! $MAC/$MAC_MASK -j DROP 
111
    if [ ! -z $GATEWAY ]; then 
112
      ebtables -A $TO -s $ROUTER_MAC -j ACCEPT 
113
    fi
114
  fi
115
}
116

  
117
#FIXME: import router mac from the config files
118
#       must know node group!! how???
119
ROUTER_MAC=6e:10:e1:a0:c3:0f
120
MAC_MASK=ff:ff:ff:0:0:0
121

  
122
TABLE=rt_$NETWORK
123

  
124
source /var/lib/snf-network/networks/$NETWORK
125

  
126

  
127
if [ "$MODE" = "routed" ]; then
128
	# special proxy-ARP/NDP routing mode
129

  
130
	# use a constant predefined MAC address for the tap
131
	ip link set $INTERFACE addr $TAP_CONSTANT_MAC
132
	# bring the tap up
133
	ifconfig $INTERFACE 0.0.0.0 up
134

  
135
	# Drop unicast BOOTP/DHCP packets
136
	iptables -D FORWARD -i $INTERFACE -p udp --dport 67 -j DROP 2>/dev/null
137
	iptables -A FORWARD -i $INTERFACE -p udp --dport 67 -j DROP
138

  
139
	routed_setup_ipv4
140
	routed_setup_ipv6
141
	routed_setup_firewall
142
	routed_setup_nfdhcpd $INTERFACE
143
elif [ "$MODE" = "bridged" ]; then
144
  while ip rule del dev $INTERFACE; do :; done
145
	ifconfig $INTERFACE 0.0.0.0 up
146
	brctl addif $BRIDGE $INTERFACE
147
	routed_setup_nfdhcpd $BRIDGE
148
  make_ebtables
149
fi   
b/modify-network
1
#!/bin/bash
2

  
3
DIR=/var/lib/snf-network
4
NEW_GATEWAY=$1
5
NEW_TYPE=$2
6
NETWORK=$3
7
RT_TABLES=/etc/iproute2/rt_tables
8

  
9
if [ $# -ne 3 ]; then
10
  echo "$0 <gateway> <private/public> <name>"
11
  exit 1
12
fi
13

  
14
source /etc/default/snf-network
15

  
16
NETWORK_FILE=$DIR/networks/$NETWORK
17

  
18
source $NETWORK_FILE
19

  
20
OLD_GATEWAY=$GATEWAY
21
OLD_TYPE=$TYPE
22

  
23
INTERFACES=$(ls $DIR/interfaces/$NETWORK-*)
24

  
25

  
26
for IFACES in $INTERFACES ; do
27
  
28
  NODEGROUP=$(echo $IFACES | sed 's/.*interfaces.*-//')
29
  source $DIR/nodegroups/$NODEGROUP
30

  
31
  read x VLAN MODE BRIDGE < $INTERFACES
32

  
33
  if [ $MODE == "routed" ]; then 
34
    if [ $TYPE == "public" ]; then
35
      ip route replace default via $GATEWAY dev $VLAN table rt_$NETWORK
36
    fi
37
  fi
38

  
39
  if [ $MODE == "bridged" ]; then
40
    if [ ! -z $GATEWAY ]; then
41
      ip route replace default via $GATEWAY dev $BRIDGE table rt_$NETWORK
42
      if [ $TYPE == "private" ]; then 
43
        if [ ! -z $ROUTER ]; then 
44
          if [ $(hostname) == $ROUTER ]; then
45
            NETMASK=$(ipcalc $SUBNET | grep Netmask | awk '{print $4}')
46
            ip addr del $GATEWAY/$NETMASK dev $BRIDGE  
47
            ip addr add $NEW_GATEWAY/$NETMASK dev $BRIDGE  
48
          fi  
49
        fi
50
      fi
51
    fi
52
  fi
53

  
54
  if [ ! -z $NEW_GATEWAY ]; then 
55
    sed -i '/^GATEWAY/ s/=.*/='"$NEW_GATEWAY"'/' $NETWORK_FILE 
56
  fi
57

  
58
  if [ ! -z $NEW_TYPE ]; then
59
    sed -i '/^TYPE/ s/=.*/='"$NEW_TYPE"'/' $NETWORK_FILE
60
  fi 
61

  
62
done
b/nfdhcpd/nfdhcpd
1
#!/usr/bin/env python
2
#
3

  
4
# nfdcpd: A promiscuous, NFQUEUE-based DHCP server for virtual machine hosting
5
# Copyright (c) 2010 GRNET SA
6
#
7
#    This program is free software; you can redistribute it and/or modify
8
#    it under the terms of the GNU General Public License as published by
9
#    the Free Software Foundation; either version 2 of the License, or
10
#    (at your option) any later version.
11
#
12
#    This program is distributed in the hope that it will be useful,
13
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
14
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
#    GNU General Public License for more details.
16
#
17
#    You should have received a copy of the GNU General Public License along
18
#    with this program; if not, write to the Free Software Foundation, Inc.,
19
#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20
#
21

  
22
import os
23
import re
24
import sys
25
import glob
26
import time
27
import logging
28
import logging.handlers
29
import threading
30
import traceback
31
import subprocess
32

  
33
import daemon
34
import daemon.pidlockfile
35
import nfqueue
36
import pyinotify
37

  
38
import IPy
39
import socket
40
from select import select
41
from socket import AF_INET, AF_INET6
42

  
43
from scapy.data import ETH_P_ALL
44
from scapy.packet import BasePacket
45
from scapy.layers.l2 import Ether
46
from scapy.layers.inet import IP, UDP
47
from scapy.layers.inet6 import IPv6, ICMPv6ND_RA, ICMPv6ND_NA, \
48
                               ICMPv6NDOptDstLLAddr, \
49
                               ICMPv6NDOptPrefixInfo, \
50
                               ICMPv6NDOptRDNSS
51
from scapy.layers.dhcp import BOOTP, DHCP
52

  
53
DEFAULT_CONFIG = "/etc/nfdhcpd/nfdhcpd.conf"
54
DEFAULT_PATH = "/var/run/ganeti-dhcpd"
55
DEFAULT_USER = "nobody"
56
DEFAULT_LEASE_LIFETIME = 604800 # 1 week
57
DEFAULT_LEASE_RENEWAL = 600  # 10 min
58
DEFAULT_RA_PERIOD = 300 # seconds
59
DHCP_DUMMY_SERVER_IP = "1.2.3.4"
60

  
61
LOG_FILENAME = "nfdhcpd.log"
62

  
63
SYSFS_NET = "/sys/class/net"
64

  
65
LOG_FORMAT = "%(asctime)-15s %(levelname)-6s %(message)s"
66

  
67
# Configuration file specification (see configobj documentation)
68
CONFIG_SPEC = """
69
[general]
70
pidfile = string()
71
datapath = string()
72
logdir = string()
73
user = string()
74

  
75
[dhcp]
76
enable_dhcp = boolean(default=True)
77
lease_lifetime = integer(min=0, max=4294967295)
78
lease_renewal = integer(min=0, max=4294967295)
79
server_ip = ip_addr()
80
dhcp_queue = integer(min=0, max=65535)
81
nameservers = ip_addr_list(family=4)
82

  
83
[ipv6]
84
enable_ipv6 = boolean(default=True)
85
ra_period = integer(min=1, max=4294967295)
86
rs_queue = integer(min=0, max=65535)
87
ns_queue = integer(min=0, max=65535)
88
nameservers = ip_addr_list(family=6)
89
"""
90

  
91

  
92
DHCPDISCOVER = 1
93
DHCPOFFER = 2
94
DHCPREQUEST = 3
95
DHCPDECLINE = 4
96
DHCPACK = 5
97
DHCPNAK = 6
98
DHCPRELEASE = 7
99
DHCPINFORM = 8
100

  
101
DHCP_TYPES = {
102
    DHCPDISCOVER: "DHCPDISCOVER",
103
    DHCPOFFER: "DHCPOFFER",
104
    DHCPREQUEST: "DHCPREQUEST",
105
    DHCPDECLINE: "DHCPDECLINE",
106
    DHCPACK: "DHCPACK",
107
    DHCPNAK: "DHCPNAK",
108
    DHCPRELEASE: "DHCPRELEASE",
109
    DHCPINFORM: "DHCPINFORM",
110
}
111

  
112
DHCP_REQRESP = {
113
    DHCPDISCOVER: DHCPOFFER,
114
    DHCPREQUEST: DHCPACK,
115
    DHCPINFORM: DHCPACK,
116
    }
117

  
118

  
119
def parse_routing_table(table="main", family=4):
120
    """ Parse the given routing table to get connected route, gateway and
121
    default device.
122

  
123
    """
124
    ipro = subprocess.Popen(["ip", "-%d" % family, "ro", "ls",
125
                             "table", table], stdout=subprocess.PIPE)
126
    routes = ipro.stdout.readlines()
127

  
128
    def_gw = None
129
    def_dev = None
130
    def_net = None
131

  
132
    for route in routes:
133
        match = re.match(r'^default.*via ([^\s]+).*dev ([^\s]+)', route)
134
        if match:
135
            def_gw, def_dev = match.groups()
136
            break
137

  
138
    for route in routes:
139
        # Find the least-specific connected route
140
        m = re.match("^([^\\s]+) dev %s" % def_dev, route)
141
        if not m:
142
            continue
143

  
144
        if family == 6 and m.group(1).startswith("fe80:"):
145
            # Skip link-local declarations in "main" table
146
            continue
147

  
148
        def_net = m.group(1)
149

  
150
        try:
151
            def_net = IPy.IP(def_net)
152
        except ValueError, e:
153
            logging.warn("Unable to parse default route entry %s: %s",
154
                         def_net, str(e))
155

  
156
    return Subnet(net=def_net, gw=def_gw, dev=def_dev)
157

  
158

  
159
def parse_binding_file(path):
160
    """ Read a client configuration from a tap file
161

  
162
    """
163
    try:
164
        iffile = open(path, 'r')
165
    except EnvironmentError, e:
166
        logging.warn("Unable to open binding file %s: %s", path, str(e))
167
        return None
168

  
169
    ifname = os.path.basename(path)
170
    mac = None
171
    ips = None
172
    link = None
173
    hostname = None
174

  
175
    for line in iffile:
176
        if line.startswith("IP="):
177
            ip = line.strip().split("=")[1]
178
            ips = ip.split()
179
        elif line.startswith("MAC="):
180
            mac = line.strip().split("=")[1]
181
        elif line.startswith("LINK="):
182
            link = line.strip().split("=")[1]
183
        elif line.startswith("HOSTNAME="):
184
            hostname = line.strip().split("=")[1]
185
        elif line.startswith("IFACE="):
186
            iface = line.strip().split("=")[1]
187

  
188
    return Client(ifname=ifname, mac=mac, ips=ips, link=link, hostname=hostname, iface=iface)
189

  
190

  
191
class ClientFileHandler(pyinotify.ProcessEvent):
192
    def __init__(self, server):
193
        pyinotify.ProcessEvent.__init__(self)
194
        self.server = server
195

  
196
    def process_IN_DELETE(self, event): # pylint: disable=C0103
197
        """ Delete file handler
198

  
199
        Currently this removes an interface from the watch list
200

  
201
        """
202
        self.server.remove_iface(event.name)
203

  
204
    def process_IN_CLOSE_WRITE(self, event): # pylint: disable=C0103
205
        """ Add file handler
206

  
207
        Currently this adds an interface to the watch list
208

  
209
        """
210
        self.server.add_iface(os.path.join(event.path, event.name))
211

  
212

  
213
class Client(object):
214
    def __init__(self, ifname=None, mac=None, ips=None, link=None, hostname=None, iface=None):
215
        self.mac = mac
216
        self.ips = ips
217
        self.hostname = hostname
218
        self.link = link
219
        self.iface = iface
220
        self.ifname = ifname
221

  
222
    @property
223
    def ip(self):
224
        return self.ips[0]
225

  
226
    def is_valid(self):
227
        return self.mac is not None and self.ips is not None\
228
               and self.hostname is not None
229

  
230

  
231
class Subnet(object):
232
    def __init__(self, net=None, gw=None, dev=None):
233
        if isinstance(net, str):
234
            self.net = IPy.IP(net)
235
        else:
236
            self.net = net
237
        self.gw = gw
238
        self.dev = dev
239

  
240
    @property
241
    def netmask(self):
242
        """ Return the netmask in textual representation
243

  
244
        """
245
        return str(self.net.netmask())
246

  
247
    @property
248
    def broadcast(self):
249
        """ Return the broadcast address in textual representation
250

  
251
        """
252
        return str(self.net.broadcast())
253

  
254
    @property
255
    def prefix(self):
256
        """ Return the network as an IPy.IP
257

  
258
        """
259
        return self.net.net()
260

  
261
    @property
262
    def prefixlen(self):
263
        """ Return the prefix length as an integer
264

  
265
        """
266
        return self.net.prefixlen()
267

  
268
    @staticmethod
269
    def _make_eui64(net, mac):
270
        """ Compute an EUI-64 address from an EUI-48 (MAC) address
271

  
272
        """
273
        comp = mac.split(":")
274
        prefix = IPy.IP(net).net().strFullsize().split(":")[:4]
275
        eui64 = comp[:3] + ["ff", "fe"] + comp[3:]
276
        eui64[0] = "%02x" % (int(eui64[0], 16) ^ 0x02)
277
        for l in range(0, len(eui64), 2):
278
            prefix += ["".join(eui64[l:l+2])]
279
        return IPy.IP(":".join(prefix))
280

  
281
    def make_eui64(self, mac):
282
        """ Compute an EUI-64 address from an EUI-48 (MAC) address in this
283
        subnet.
284

  
285
        """
286
        return self._make_eui64(self.net, mac)
287

  
288
    def make_ll64(self, mac):
289
        """ Compute an IPv6 Link-local address from an EUI-48 (MAC) address
290

  
291
        """
292
        return self._make_eui64("fe80::", mac)
293

  
294

  
295
class VMNetProxy(object): # pylint: disable=R0902
296
    def __init__(self, data_path, dhcp_queue_num=None, # pylint: disable=R0913
297
                 rs_queue_num=None, ns_queue_num=None,
298
                 dhcp_lease_lifetime=DEFAULT_LEASE_LIFETIME,
299
                 dhcp_lease_renewal=DEFAULT_LEASE_RENEWAL,
300
                 dhcp_server_ip=DHCP_DUMMY_SERVER_IP, dhcp_nameservers=None,
301
                 ra_period=DEFAULT_RA_PERIOD, ipv6_nameservers=None):
302

  
303
        self.data_path = data_path
304
        self.lease_lifetime = dhcp_lease_lifetime
305
        self.lease_renewal = dhcp_lease_renewal
306
        self.dhcp_server_ip = dhcp_server_ip
307
        self.ra_period = ra_period
308
        if dhcp_nameservers is None:
309
            self.dhcp_nameserver = []
310
        else:
311
            self.dhcp_nameservers = dhcp_nameservers
312

  
313
        if ipv6_nameservers is None:
314
            self.ipv6_nameservers = []
315
        else:
316
            self.ipv6_nameservers = ipv6_nameservers
317

  
318
        self.ipv6_enabled = False
319

  
320
        self.clients = {}
321
        self.subnets = {}
322
        self.ifaces = {}
323
        self.v6nets = {}
324
        self.nfq = {}
325
        self.l2socket = socket.socket(socket.AF_PACKET,
326
                                      socket.SOCK_RAW, ETH_P_ALL)
327
        self.l2socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 0)
328

  
329
        # Inotify setup
330
        self.wm = pyinotify.WatchManager()
331
        mask = pyinotify.EventsCodes.ALL_FLAGS["IN_DELETE"]
332
        mask |= pyinotify.EventsCodes.ALL_FLAGS["IN_CLOSE_WRITE"]
333
        inotify_handler = ClientFileHandler(self)
334
        self.notifier = pyinotify.Notifier(self.wm, inotify_handler)
335
        self.wm.add_watch(self.data_path, mask, rec=True)
336

  
337
        # NFQUEUE setup
338
        if dhcp_queue_num is not None:
339
            self._setup_nfqueue(dhcp_queue_num, AF_INET, self.dhcp_response)
340

  
341
        if rs_queue_num is not None:
342
            self._setup_nfqueue(rs_queue_num, AF_INET6, self.rs_response)
343
            self.ipv6_enabled = True
344

  
345
        if ns_queue_num is not None:
346
            self._setup_nfqueue(ns_queue_num, AF_INET6, self.ns_response)
347
            self.ipv6_enabled = True
348

  
349
    def _cleanup(self):
350
        """ Free all resources for a graceful exit
351

  
352
        """
353
        logging.info("Cleaning up")
354

  
355
        logging.debug("Closing netfilter queues")
356
        for q in self.nfq.values():
357
            q.close()
358

  
359
        logging.debug("Closing socket")
360
        self.l2socket.close()
361

  
362
        logging.debug("Stopping inotify watches")
363
        self.notifier.stop()
364

  
365
        logging.info("Cleanup finished")
366

  
367
    def _setup_nfqueue(self, queue_num, family, callback):
368
        logging.debug("Setting up NFQUEUE for queue %d, AF %s",
369
                      queue_num, family)
370
        q = nfqueue.queue()
371
        q.set_callback(callback)
372
        q.fast_open(queue_num, family)
373
        q.set_queue_maxlen(5000)
374
        # This is mandatory for the queue to operate
375
        q.set_mode(nfqueue.NFQNL_COPY_PACKET)
376
        self.nfq[q.get_fd()] = q
377

  
378
    def sendp(self, data, iface):
379
        """ Send a raw packet using a layer-2 socket
380

  
381
        """
382
        if isinstance(data, BasePacket):
383
            data = str(data)
384

  
385
        self.l2socket.bind((iface, ETH_P_ALL))
386
        count = self.l2socket.send(data)
387
        ldata = len(data)
388
        if count != ldata:
389
            logging.warn("Truncated send on %s (%d/%d bytes sent)",
390
                         iface, count, ldata)
391

  
392
    def build_config(self):
393
        self.clients.clear()
394
        self.subnets.clear()
395

  
396
        for path in glob.glob(os.path.join(self.data_path, "*")):
397
            self.add_iface(path)
398

  
399
    def get_ifindex(self, iface):
400
        """ Get the interface index from sysfs
401

  
402
        """
403
        path = os.path.abspath(os.path.join(SYSFS_NET, iface, "ifindex"))
404
        if not path.startswith(SYSFS_NET):
405
            return None
406

  
407
        ifindex = None
408

  
409
        try:
410
            f = open(path, 'r')
411
        except EnvironmentError:
412
            logging.debug("%s is probably down, removing", iface)
413
            self.remove_iface(iface)
414

  
415
            return ifindex
416

  
417
        try:
418
            ifindex = f.readline().strip()
419
            try:
420
                ifindex = int(ifindex)
421
            except ValueError, e:
422
                logging.warn("Failed to get ifindex for %s, cannot parse sysfs"
423
                             " output '%s'", iface, ifindex)
424
        except EnvironmentError, e:
425
            logging.warn("Error reading %s's ifindex from sysfs: %s",
426
                         iface, str(e))
427
            self.remove_iface(iface)
428
        finally:
429
            f.close()
430

  
431
        return ifindex
432

  
433

  
434
    def get_iface_hw_addr(self, iface):
435
        """ Get the interface hardware address from sysfs
436

  
437
        """
438
        path = os.path.abspath(os.path.join(SYSFS_NET, iface, "address"))
439
        if not path.startswith(SYSFS_NET):
440
            return None
441

  
442
        addr = None
443
        try:
444
            f = open(path, 'r')
445
        except EnvironmentError:
446
            logging.debug("%s is probably down, removing", iface)
447
            self.remove_iface(iface)
448
            return addr
449

  
450
        try:
451
            addr = f.readline().strip()
452
        except EnvironmentError, e:
453
            logging.warn("Failed to read hw address for %s from sysfs: %s",
454
                         iface, str(e))
455
        finally:
456
            f.close()
457

  
458
        return addr
459

  
460
    def add_iface(self, path):
461
        """ Add an interface to monitor
462

  
463
        """
464
        iface = os.path.basename(path)
465

  
466
        logging.debug("Updating configuration for %s", iface)
467
        binding = parse_binding_file(path)
468
        if binding is None:
469
            return
470
        ifindex = self.get_ifindex(binding.iface)
471

  
472
        if ifindex is None:
473
            logging.warn("Stale configuration for %s found", iface)
474
        else:
475
            if binding.is_valid():
476
                self.clients[binding.mac] = binding
477
                self.subnets[binding.link] = parse_routing_table(binding.link)
478
                logging.debug("Added client %s on %s", binding.hostname, iface)
479
                self.ifaces[ifindex] = binding.iface
480
                self.v6nets[iface] = parse_routing_table(binding.link, 6)
481

  
482
    def remove_iface(self, ifname):
483
        """ Cleanup clients on a removed interface
484

  
485
        """
486
        if ifname in self.v6nets:
487
            del self.v6nets[ifname]
488

  
489
        for mac in self.clients.keys():
490
            if self.clients[mac].ifname == ifname:
491
                iface = self.client[mac].iface
492
                del self.clients[mac]
493

  
494
        for ifindex in self.ifaces.keys():
495
            if self.ifaces[ifindex] == ifname == iface:
496
                del self.ifaces[ifindex]
497

  
498
        logging.debug("Removed interface %s", ifname)
499

  
500
    def dhcp_response(self, i, payload): # pylint: disable=W0613,R0914
501
        """ Generate a reply to a BOOTP/DHCP request
502

  
503
        """
504
        logging.info("%s",payload)
505
        indev = payload.get_indev()
506
        try:
507
            # Get the actual interface from the ifindex
508
            iface = self.ifaces[indev]
509
        except KeyError:
510
            # We don't know anything about this interface, so accept the packet
511
            # and return
512
            logging.debug("Ignoring DHCP request on unknown iface %d", indev)
513
            # We don't know what to do with this packet, so let the kernel
514
            # handle it
515
            payload.set_verdict(nfqueue.NF_ACCEPT)
516
            return
517

  
518
        # Decode the response - NFQUEUE relays IP packets
519
        pkt = IP(payload.get_data())
520

  
521
        # Signal the kernel that it shouldn't further process the packet
522
        payload.set_verdict(nfqueue.NF_DROP)
523

  
524
        # Get the client MAC address
525
        resp = pkt.getlayer(BOOTP).copy()
526
        hlen = resp.hlen
527
        mac = resp.chaddr[:hlen].encode("hex")
528
        mac, _ = re.subn(r'([0-9a-fA-F]{2})', r'\1:', mac, hlen-1)
529

  
530
	logging.info("%s %s %s ", resp, hlen, mac)
531
        # Server responses are always BOOTREPLYs
532
        resp.op = "BOOTREPLY"
533
        del resp.payload
534

  
535
        try:
536
            binding = self.clients[mac]
537
        except KeyError:
538
            logging.warn("Invalid client %s on %s", mac, iface)
539
            return
540

  
541
        if iface != binding.iface:
542
            logging.warn("Received spoofed DHCP request for %s from interface"
543
                         " %s instead of %s", mac, iface, binding.iface)
544
            return
545

  
546
        resp = Ether(dst=mac, src=self.get_iface_hw_addr(iface))/\
547
               IP(src=DHCP_DUMMY_SERVER_IP, dst=binding.ip)/\
548
               UDP(sport=pkt.dport, dport=pkt.sport)/resp
549
        subnet = self.subnets[binding.link]
550

  
551
        if not DHCP in pkt:
552
            logging.warn("Invalid request from %s on %s, no DHCP"
553
                         " payload found", binding.mac, iface)
554
            return
555

  
556
        dhcp_options = []
557
        requested_addr = binding.ip
558
        for opt in pkt[DHCP].options:
559
            if type(opt) is tuple and opt[0] == "message-type":
560
                req_type = opt[1]
561
            if type(opt) is tuple and opt[0] == "requested_addr":
562
                requested_addr = opt[1]
563

  
564
        logging.info("%s from %s on %s", DHCP_TYPES.get(req_type, "UNKNOWN"),
565
                     binding.mac, iface)
566

  
567
        if req_type == DHCPREQUEST and requested_addr != binding.ip:
568
            resp_type = DHCPNAK
569
            logging.info("Sending DHCPNAK to %s on %s: requested %s"
570
                         " instead of %s", binding.mac, iface, requested_addr,
571
                         binding.ip)
572

  
573
        elif req_type in (DHCPDISCOVER, DHCPREQUEST):
574
            resp_type = DHCP_REQRESP[req_type]
575
            resp.yiaddr = self.clients[mac].ip
576
            dhcp_options += [
577
                 ("hostname", binding.hostname),
578
                 ("domain", binding.hostname.split('.', 1)[-1]),
579
                 ("router", subnet.gw),
580
                 ("broadcast_address", str(subnet.broadcast)),
581
                 ("subnet_mask", str(subnet.netmask)),
582
                 ("renewal_time", self.lease_renewal),
583
                 ("lease_time", self.lease_lifetime),
584
            ]
585
            dhcp_options += [("name_server", x) for x in self.dhcp_nameservers]
586

  
587
        elif req_type == DHCPINFORM:
588
            resp_type = DHCP_REQRESP[req_type]
589
            dhcp_options += [
590
                 ("hostname", binding.hostname),
591
                 ("domain", binding.hostname.split('.', 1)[-1]),
592
            ]
593
            dhcp_options += [("name_server", x) for x in self.dhcp_nameservers]
594

  
595
        elif req_type == DHCPRELEASE:
596
            # Log and ignore
597
            logging.info("DHCPRELEASE from %s on %s", binding.mac, iface)
598
            return
599

  
600
        # Finally, always add the server identifier and end options
601
        dhcp_options += [
602
            ("message-type", resp_type),
603
            ("server_id", DHCP_DUMMY_SERVER_IP),
604
            "end"
605
        ]
606
        resp /= DHCP(options=dhcp_options)
607

  
608
        logging.info("%s to %s (%s) on %s", DHCP_TYPES[resp_type], mac,
609
                     binding.ip, iface)
610
        self.sendp(resp, iface)
611

  
612
    def rs_response(self, i, payload): # pylint: disable=W0613
613
        """ Generate a reply to a BOOTP/DHCP request
614

  
615
        """
616
        indev = payload.get_indev()
617
        try:
618
            # Get the actual interface from the ifindex
619
            iface = self.ifaces[indev]
620
        except KeyError:
621
            logging.debug("Ignoring router solicitation on"
622
                          " unknown interface %d", indev)
623
            # We don't know what to do with this packet, so let the kernel
624
            # handle it
625
            payload.set_verdict(nfqueue.NF_ACCEPT)
626
            return
627

  
628
        ifmac = self.get_iface_hw_addr(iface)
629
        subnet = self.v6nets[iface]
630
        ifll = subnet.make_ll64(ifmac)
631

  
632
        # Signal the kernel that it shouldn't further process the packet
633
        payload.set_verdict(nfqueue.NF_DROP)
634

  
635
        resp = Ether(src=self.get_iface_hw_addr(iface))/\
636
               IPv6(src=str(ifll))/ICMPv6ND_RA(routerlifetime=14400)/\
637
               ICMPv6NDOptPrefixInfo(prefix=str(subnet.prefix),
638
                                     prefixlen=subnet.prefixlen)
639

  
640
        if self.ipv6_nameservers:
641
            resp /= ICMPv6NDOptRDNSS(dns=self.ipv6_nameservers,
642
                                     lifetime=self.ra_period * 3)
643

  
644
        logging.info("RA on %s for %s", iface, subnet.net)
645
        self.sendp(resp, iface)
646

  
647
    def ns_response(self, i, payload): # pylint: disable=W0613
648
        """ Generate a reply to an ICMPv6 neighbor solicitation
649

  
650
        """
651
        indev = payload.get_indev()
652
        try:
653
            # Get the actual interface from the ifindex
654
            iface = self.ifaces[indev]
655
        except KeyError:
656
            logging.debug("Ignoring neighbour solicitation on"
657
                          " unknown interface %d", indev)
658
            # We don't know what to do with this packet, so let the kernel
659
            # handle it
660
            payload.set_verdict(nfqueue.NF_ACCEPT)
661
            return
662

  
663
        ifmac = self.get_iface_hw_addr(iface)
664
        subnet = self.v6nets[iface]
665
        ifll = subnet.make_ll64(ifmac)
666

  
667
        ns = IPv6(payload.get_data())
668

  
669
        if not (subnet.net.overlaps(ns.tgt) or str(ns.tgt) == str(ifll)):
670
            logging.debug("Received NS for a non-routable IP (%s)", ns.tgt)
671
            payload.set_verdict(nfqueue.NF_ACCEPT)
672
            return 1
673

  
674
        payload.set_verdict(nfqueue.NF_DROP)
675

  
676
        try:
677
            client_lladdr = ns.lladdr
678
        except AttributeError:
679
            return 1
680

  
681
        resp = Ether(src=ifmac, dst=client_lladdr)/\
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff