Skip to content

Instantly share code, notes, and snippets.

@kamilion
Created March 23, 2014 05:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save kamilion/9719218 to your computer and use it in GitHub Desktop.
Save kamilion/9719218 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python
# SLLabs.com vif hotplug script
# Copyright (C) 2013 SLLabs.com <support@sllabs.com>
# This work is free. You can redistribute it and/or modify it under the
# terms of the Do What The Fuck You Want To Public License, Version 2,
# as published by Sam Hocevar. See http://www.wtfpl.net/ for more details.
# Written in 2013 for Kamilion@SLLabs.com by ircs://irc.ospnet.org/#sllabs residents
# "If you have an apple and I have an apple and we exchange these apples
# then you and I will still each have one apple.
# But if you have an idea and I have an idea and we exchange these ideas,
# then each of us will have two ideas." -- George Bernard Shaw
import os, sys, re, json
from fcntl import flock, LOCK_EX, LOCK_UN
from sh import brctl, xenstore_read, xenstore_write, xenstore_ls, ip, ebtables
from netaddr import EUI, IPNetwork
def load_vifdata():
vifdata = {}
for i in xenstore_ls(os.environ['XENBUS_PATH']):
match = re.match(r'^\s*([^\s]+) = "([^"]+)"\s*$', i)
if match is not None:
vifdata[match.group(1)] = match.group(2)
return vifdata
def load_ipconf(domain):
ipconf = {}
with open('/vms/active/' + domain + '/ips.conf', 'r') as handle:
ipconf = json.load(handle)
return ipconf
def add_filter(chain):
ebtables('-N', chain + '-filter', '-P', 'RETURN')
ebtables('-A', chain + '-filter', '-j', 'RETURN')
ebtables('-A', chain, '-j', chain + '-filter')
def add_ipv6(ipconf, vifdata, vifindex, chain, direction):
if 'ipv6' in ipconf[vifindex]:
mac = EUI(vifdata['mac'])
linklocal = mac.ipv6_link_local()
snma = IPNetwork('ff02::1:ff00:0/104')[int(mac) % 16777216]
ebtables('-A', chain, '-p', 'IPv6', direction, ipconf[vifindex]['ipv6'], '-j', 'ACCEPT')
ebtables('-A', chain, '-p', 'IPv6', direction, str(linklocal), '-j', 'ACCEPT')
ebtables('-A', chain, '-p', 'IPv6', direction, str(snma), '-j', 'ACCEPT')
def add_ipv4(ipconf, vifindex, chain, direction):
ebtables('-A', chain, '-p', 'IPv4', '--' + direction, ipconf[vifindex]['ip'], '-j', 'ACCEPT')
ebtables('-A', chain, '-p', 'ARP', '--arp-' + direction, ipconf[vifindex]['ip'], '-j', 'ACCEPT')
def online():
vif = os.environ['vif']
fromvif = 'from-' + vif
tovif = 'to-' + vif
vifdata = load_vifdata()
vifindex = int(vifdata['handle'])
domid = vifdata['frontend-id']
domain = str(xenstore_read('/local/domain/' + domid + '/name')).rstrip()
ipconf = load_ipconf(domain)
lockfile = open('/var/lock/ebtables', 'w')
flock(lockfile, LOCK_EX)
ebtables('-N', fromvif, '-P', 'DROP')
ebtables('-A', fromvif, '-s', '!', vifdata['mac'], '-j', 'DROP')
ebtables('-A', fromvif, '-p', 'IPv4', '--ip-src', '0.0.0.0', '--ip-dst', '255.255.255.255', '--ip-proto', 'UDP', '--ip-sport', '68', '--ip-dport', '67', '-j', 'ACCEPT')
add_filter(fromvif)
fromviflimit = fromvif + '-limit'
ebtables('-N', fromviflimit, '-P', 'DROP')
ebtables('-A', fromviflimit, '--limit', '10000/second', '--limit-burst', '5000', '-j', 'RETURN')
ebtables('-A', fromviflimit, '--limit', '10000/second', '--limit-burst', '5000', '-j', 'RETURN')
ebtables('-A', fromviflimit, '--limit', '10000/second', '--limit-burst', '5000', '-j', 'RETURN')
ebtables('-A', fromviflimit, '-j', 'DROP')
ebtables('-A', fromvif, '-j', fromvif + '-limit')
add_ipv6(ipconf, vifdata, vifindex, fromvif, '--ip6-src')
add_ipv4(ipconf, vifindex, fromvif, 'ip-src')
ebtables('-A', fromvif, '-j', 'DROP')
ebtables('-A', 'INPUT', '-i', vif, '-j', fromvif)
ebtables('-A', 'FORWARD', '-i', vif, '-j', fromvif)
ebtables('-N', tovif, '-P', 'DROP')
ebtables('-A', tovif, '-p', 'IPv4', '--ip-dst', '255.255.255.255', '--ip-proto', 'UDP', '--ip-sport', '67', '--ip-dport', '68', '-j', 'ACCEPT')
add_filter(tovif)
add_ipv6(ipconf, vifdata, vifindex, tovif, '--ip6-dst')
add_ipv4(ipconf, vifindex, tovif, 'ip-dst')
ebtables('-A', tovif, '-j', 'DROP')
ebtables('-A', 'OUTPUT', '-o', vif, '-j', tovif)
ebtables('-A', 'FORWARD', '-o', vif, '-j', tovif)
flock(lockfile, LOCK_UN)
lockfile.close()
ip('link', 'set', vif, 'up')
brctl('addif', ipconf[vifindex]['bridge'], vif)
xenstore_write(os.environ['XENBUS_PATH'] + '/hotplug-status', 'connected')
def offline():
vif = os.environ['vif']
fromvif = 'from-' + vif
tovif = 'to-' + vif
vifdata = load_vifdata()
vifindex = int(vifdata['handle'])
domid = vifdata['frontend-id']
domain = str(xenstore_read('/local/domain/' + domid + '/name')).rstrip()
ipconf = load_ipconf(domain)
brctl('delif', ipconf[vifindex]['bridge'], vif, _ok_code=[0,1])
ip('link', 'set', vif, 'down', _ok_code=[0,255])
lockfile = open('/var/lock/ebtables', 'w')
flock(lockfile, LOCK_EX)
ebtables('-D', 'INPUT', '-i', vif, '-j', fromvif)
ebtables('-D', 'FORWARD', '-i', vif, '-j', fromvif)
ebtables('-X', fromvif)
ebtables('-X', fromvif + '-filter')
ebtables('-X', fromvif + '-limit')
ebtables('-D', 'OUTPUT', '-o', vif, '-j', tovif)
ebtables('-D', 'FORWARD', '-o', vif, '-j', tovif)
ebtables('-X', tovif)
ebtables('-X', tovif + '-filter')
flock(lockfile, LOCK_UN)
lockfile.close()
if sys.argv[1] == 'online':
online()
elif sys.argv[1] == 'offline':
offline()
else:
sys.exit(1)
@kode54
Copy link

kode54 commented Feb 8, 2016

What bridge or ebtables or iptables configuration do I need to make this script work? I was using it before, but lost my previous machine setup due to not backing everything up, and I didn't set it up in the first place. Now I can't get machines configured with it to talk to the bridge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment