Cut out unneeded stuff
[nodemanager.git] / net.py
1 """network configuration"""
2
3 # system provided modules
4 import os, string, time, socket
5 from socket import inet_aton
6
7 # PlanetLab system modules
8 import sioc, plnet
9
10 # local modules
11 import plnode.bwlimit as bwlimit
12 import logger, iptables, tools
13 import subprocess
14
15 # we can't do anything without a network
16 priority=1
17
18 dev_default = tools.get_default_if()
19
20
21 def start():
22     logger.log("net: plugin starting up...")
23
24 def GetSlivers(data, config, plc):
25     # added by caglar
26     # band-aid for short period as old API returns networks instead of interfaces
27     global KEY_NAME
28     KEY_NAME = "interfaces"
29     #################
30
31     logger.verbose("net: GetSlivers called.")
32     if not 'interfaces' in data:
33         # added by caglar
34         # band-aid for short period as old API returns networks instead of interfaces
35         # logger.log_missing_data('net.GetSlivers','interfaces')
36         # return
37         if not 'networks' in data:
38             logger.log_missing_data('net.GetSlivers','interfaces')
39             return
40         else:
41             KEY_NAME = "networks"
42         ##################
43
44     plnet.InitInterfaces(logger, plc, data)
45     
46     """
47     if 'OVERRIDES' in dir(config):
48         if config.OVERRIDES.get('net_max_rate') == '-1':
49             logger.log("net: Slice and node BW Limits disabled.")
50             if len(bwlimit.tc("class show dev %s" % dev_default)):
51                 logger.verbose("net: *** DISABLING NODE BW LIMITS ***")
52                 bwlimit.stop()
53         else:
54             InitNodeLimit(data)
55             InitI2(plc, data)
56     else:
57         InitNodeLimit(data)
58         InitI2(plc, data)
59     InitNAT(plc, data)
60     """
61     
62 def InitNodeLimit(data):
63
64     # query running network interfaces
65     devs = sioc.gifconf()
66     ips = dict(zip(devs.values(), devs.keys()))
67     macs = {}
68     for dev in devs:
69         macs[sioc.gifhwaddr(dev).lower()] = dev
70
71     for interface in data[KEY_NAME]:
72         # Get interface name preferably from MAC address, falling
73         # back on IP address.
74         hwaddr=interface['mac']
75         if hwaddr <> None: hwaddr=hwaddr.lower()
76         if hwaddr in macs:
77             dev = macs[interface['mac']]
78         elif interface['ip'] in ips:
79             dev = ips[interface['ip']]
80         else:
81             logger.log('net: %s: no such interface with address %s/%s' % (interface['hostname'], interface['ip'], interface['mac']))
82             continue
83
84         # Get current node cap
85         try:
86             old_bwlimit = bwlimit.get_bwcap(dev)
87         except:
88             old_bwlimit = None
89
90         # Get desired node cap
91         if interface['bwlimit'] is None or interface['bwlimit'] < 0:
92             new_bwlimit = bwlimit.bwmax
93         else:
94             new_bwlimit = interface['bwlimit']
95
96         if old_bwlimit != new_bwlimit:
97             # Reinitialize bandwidth limits
98             bwlimit.init(dev, new_bwlimit)
99
100             # XXX This should trigger an rspec refresh in case
101             # some previously invalid sliver bwlimit is now valid
102             # again, or vice-versa.
103
104 def InitI2(plc, data):
105     if not 'groups' in data: return
106
107     if "Internet2" in data['groups']:
108         logger.log("net: This is an Internet2 node.  Setting rules.")
109         i2nodes = []
110         i2nodeids = plc.GetNodeGroups(["Internet2"])[0]['node_ids']
111         for node in plc.GetInterfaces({"node_id": i2nodeids}, ["ip"]):
112             # Get the IPs
113             i2nodes.append(node['ip'])
114         # this will create the set if it doesn't already exist
115         # and add IPs that don't exist in the set rather than
116         # just recreateing the set.
117         bwlimit.exempt_init('Internet2', i2nodes)
118
119         # set the iptables classification rule if it doesnt exist.
120         cmd = '-A POSTROUTING -m set --set Internet2 dst -j CLASSIFY --set-class 0001:2000 --add-mark'
121         rules = []
122         ipt = os.popen("/sbin/iptables-save")
123         for line in ipt.readlines(): rules.append(line.strip(" \n"))
124         ipt.close()
125         if cmd not in rules:
126             logger.verbose("net:  Adding iptables rule for Internet2")
127             os.popen("/sbin/iptables -t mangle " + cmd)
128
129 def InitNAT(plc, data):
130
131     # query running network interfaces
132     devs = sioc.gifconf()
133     ips = dict(zip(devs.values(), devs.keys()))
134     macs = {}
135     for dev in devs:
136         macs[sioc.gifhwaddr(dev).lower()] = dev
137
138     ipt = iptables.IPTables()
139     for interface in data[KEY_NAME]:
140         # Get interface name preferably from MAC address, falling
141         # back on IP address.
142         hwaddr=interface['mac']
143         if hwaddr <> None: hwaddr=hwaddr.lower()
144         if hwaddr in macs:
145             dev = macs[interface['mac']]
146         elif interface['ip'] in ips:
147             dev = ips[interface['ip']]
148         else:
149             logger.log('net: %s: no such interface with address %s/%s' % (interface['hostname'], interface['ip'], interface['mac']))
150             continue
151
152         try:
153             settings = plc.GetInterfaceTags({'interface_tag_id': interface['interface_tag_ids']})
154         except:
155             continue
156
157         for setting in settings:
158             if setting['category'].upper() != 'FIREWALL':
159                 continue
160             if setting['name'].upper() == 'EXTERNAL':
161                 # Enable NAT for this interface
162                 ipt.add_ext(dev)
163             elif setting['name'].upper() == 'INTERNAL':
164                 ipt.add_int(dev)
165             elif setting['name'].upper() == 'PF': # XXX Uglier code is hard to find...
166                 for pf in setting['value'].split("\n"):
167                     fields = {}
168                     for field in pf.split(","):
169                         (key, val) = field.split("=", 2)
170                         fields[key] = val
171                     if 'new_dport' not in fields:
172                         fields['new_dport'] = fields['dport']
173                     if 'source' not in fields:
174                         fields['source'] = "0.0.0.0/0"
175                     ipt.add_pf(fields)
176     ipt.commit()
177
178 # Helper functions for converting to CIDR notation
179 def get_net_size(netmask):
180     binary_str = ''
181     for octet in netmask:
182         binary_str += bin(int(octet))[2:].zfill(8)
183     return str(len(binary_str.rstrip('0')))
184
185 def to_cidr(ipaddr, netmask):
186     # validate input
187     inet_aton(ipaddr)
188     inet_aton(netmask)
189
190     ipaddr = ipaddr.split('.')
191     netmask = netmask.split('.')
192
193     net_start = [str(int(ipaddr[x]) & int(netmask[x])) for x in range(0,4)]
194     return '.'.join(net_start) + '/' + get_net_size(netmask)
195
196 def ipaddr_range(network, broadcast):
197     start = network.split('.')
198     end = broadcast.split('.')
199
200     # Assume interface always claims the first address in the block
201     start[3] = str(int(start[3]) + 2)
202     end[3] = str(int(end[3]) - 1)
203
204     return '.'.join(start) + ',' + '.'.join(end)
205