diff -r 1c5d2a037d54 gpl/lib/libnl/lib/route/route_obj.c --- a/gpl/lib/libnl/lib/route/route_obj.c Wed Aug 15 17:43:52 2012 -0600 +++ b/gpl/lib/libnl/lib/route/route_obj.c Thu Aug 16 18:36:32 2012 -0600 @@ -983,6 +983,7 @@ } if (old_nh) { + rtnl_route_nh_set_flags(old_nh, rtm->rtm_flags & 0xff); if (route->rt_nr_nh == 0) { /* If no nexthops have been provided via RTA_MULTIPATH * we add it as regular nexthop to maintain backwards @@ -1042,10 +1043,15 @@ if (route->rt_src) rtmsg.rtm_src_len = nl_addr_get_prefixlen(route->rt_src); - if (rtmsg.rtm_scope == RT_SCOPE_NOWHERE) rtmsg.rtm_scope = rtnl_route_guess_scope(route); + if (rtnl_route_get_nnexthops(route) == 1) { + struct rtnl_nexthop *nh; + nh = rtnl_route_nexthop_n(route, 0); + rtmsg.rtm_flags |= nh->rtnh_flags; + } + if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0) goto nla_put_failure; diff -r 1c5d2a037d54 lib/python-cp/_route.c --- a/lib/python-cp/_route.c Wed Aug 15 17:43:52 2012 -0600 +++ b/lib/python-cp/_route.c Thu Aug 16 18:36:32 2012 -0600 @@ -150,6 +150,8 @@ static void nl_entry_cb(struct nl_object *new, struct nl_cache_cb_arg *arg); static int nl_addr_Setter(struct nl_object *obj, PyObject *val, int hint, nl_addr_setter setter_fn); static PyObject *nla_Getter(struct nl_addr *nla); +static int sync_route(Route *route); +static struct nl_cache *fill_route_cache(void); static PyMethodDef _route_methods[] = { { "get_rules", get_rules, METH_VARARGS, "Get all routing rules." }, @@ -426,9 +428,30 @@ return res; } +static struct nl_cache *fill_route_cache(void) +{ + struct nl_sock *nls; + struct nl_cache *route_cache; + + if ((nls = py_nls_connect()) == NULL) { + return NULL; + } + + if (rtnl_route_alloc_cache(nls, 0, 0, &route_cache) < 0) { + nl_close(nls); + nl_socket_free(nls); + PyErr_Format(PyExc_IOError, "Failed to fill route cache."); + return NULL; + } + + nl_close(nls); + nl_socket_free(nls); + + return route_cache; +} + static PyObject *get_routes(PyObject *mod, PyObject *args) { - struct nl_sock *nls; struct nl_cache *route_cache; PyObject *res; struct nl_cache_cb_arg cbarg; @@ -439,14 +462,7 @@ return NULL; } - if ((nls = py_nls_connect()) == NULL) { - return NULL; - } - - if (rtnl_route_alloc_cache(nls, 0, 0, &route_cache) < 0) { - nl_close(nls); - nl_socket_free(nls); - PyErr_Format(PyExc_IOError, "Failed to fill route cache."); + if (!(route_cache = fill_route_cache())) { return NULL; } @@ -464,10 +480,7 @@ } } } - nl_cache_free(route_cache); - nl_close(nls); - nl_socket_free(nls); return res; } @@ -522,7 +535,7 @@ return NULL; } - /* store nexthop data from our list in the structure during the update. */ + /* Store nexthop data from our list in the structure during the update. */ for (i=0; i < PyList_GET_SIZE(route->nexthops); i++) { Nexthop *nexthop = (Nexthop*) PyList_GET_ITEM(route->nexthops, i); if (!PyObject_TypeCheck(nexthop, &NexthopType)) { @@ -530,18 +543,47 @@ err = 1; goto done; } - rtnl_route_add_nexthop(route->u.r, ((Nexthop*)nexthop)->nh); // borrow ref + nh = rtnl_route_nh_clone(((Nexthop*)nexthop)->nh); + if (!nh) { + PyErr_Format(PyExc_IOError, "Error cloning nexthop struct"); + err = 1; + goto done; + } + rtnl_route_add_nexthop(route->u.r, nh); } - if ((err = rtnl_route_add(nls, (route)->u.r, 0)) != 0) { + if ((err = rtnl_route_add(nls, (route)->u.r, 0))) { PyErr_Format(PyExc_IOError, "Route could not be added: %s", nl_geterror(err)); + goto done; + } + + if ((err = (sync_route(route) != 0)) != 0) { + goto done; + } + + /* Refresh nexthop data from the newly synced route. */ + if (PyList_GET_SIZE(route->nexthops) != rtnl_route_get_nnexthops(route->u.r)) { + PyErr_Format(PyExc_IOError, "Unexpected misalignment of route nexthops"); err = 1; goto done; } + for (i=0; i < PyList_GET_SIZE(route->nexthops); i++) { + Nexthop *nexthop = (Nexthop*) PyList_GET_ITEM(route->nexthops, i); + nh = rtnl_route_nh_clone(rtnl_route_nexthop_n(route->u.r, i)); + if (!nh) { + PyErr_Format(PyExc_IOError, "Error cloning nexthop struct"); + err = 1; + goto done; + } + rtnl_route_nh_free(nexthop->nh); + nexthop->family = rtnl_route_get_family(route->u.r); + nexthop->nh = nh; + } done: while ((nh = rtnl_route_nexthop_n(route->u.r, 0))) { rtnl_route_remove_nexthop(route->u.r, nh); + rtnl_route_nh_free(nh); } nl_close(nls); nl_socket_free(nls); @@ -552,6 +594,51 @@ Py_RETURN_NONE; } +/* Sync the internal info of a route from the kernel. */ +static int sync_route(Route *route) +{ + struct nl_object *x; + struct nl_cache *route_cache = fill_route_cache(); + int found = 0; + struct nl_dump_params params = { + .dp_fd = stdout, + .dp_type = NL_DUMP_DETAILS, + }; + + if (!route_cache) { + return -1; + } + + fprintf(stderr, "My route:\n"); + nl_object_dump(route->u.nlo, ¶ms); + for (x = nl_cache_get_first(route_cache); x; x = nl_cache_get_next(x)) { + struct rtnl_nexthop *nh_a = rtnl_route_nexthop_n((struct rtnl_route *)x, 0); + struct rtnl_nexthop *nh_b = rtnl_route_nexthop_n(route->u.r, 0); + fprintf(stderr, "Test route:\n"); + nl_object_dump(x, ¶ms); + fprintf(stderr, "DIFF: %x\n", nl_object_diff(x, route->u.nlo)); + fprintf(stderr, "NH DIFF: %x\n", rtnl_route_nh_compare(nh_a, nh_b, nh_b->ce_mask, 1)); + if (nl_object_match_filter(x, route->u.nlo)) { + fprintf(stderr, "found\n\n"); + nl_object_get(x); + rtnl_route_put(route->u.r); + route->u.nlo = x; + found = 1; + break; + } else { + fprintf(stderr, "mismatch\n\n"); + } + } + nl_cache_free(route_cache); + + if (!found) { + PyErr_SetString(PyExc_Exception, "Could not sync route with kernel."); + return -1; + } + + return 0; +} + static PyObject *del_rule(PyObject *mod, PyObject *args) { PyObject *rule; diff -r 1c5d2a037d54 lib/python-cp/route.py --- a/lib/python-cp/route.py Wed Aug 15 17:43:52 2012 -0600 +++ b/lib/python-cp/route.py Thu Aug 16 18:36:32 2012 -0600 @@ -26,7 +26,6 @@ import threading import socket - protocols = { 0: 'unspecified', 1: 'icmp_redirect', @@ -171,6 +170,10 @@ del_route(r) except IOError as e: pass + self.refresh() + if self.routes: + raise Exception("Failed to flush route table.") + def addRoute(self, route=None, **kwargs): """ Add a route to this table. If the route arg is None then we will @@ -200,17 +203,10 @@ if onlink: nh.flags = 'onlink' - if netmask: - try: - netmask = int(netmask) - except: - netmask = ipcalc.Network(0, netmask).mask - ip = '%s/%d' % (ip, netmask) + r = Route() + r.dst = self._dstTranslate(ip, netmask) + r.nexthops.append(nh) - r = Route() - if ip: - r.dst = ip - r.nexthops.append(nh) return r def removeRoute(self, route): @@ -218,18 +214,14 @@ del_route(route) self.routes.remove(route) - def getRoute(self, ip=None, netmask=None, gw=None, iface=None): + def getRoute(self, ip=None, netmask=None): - self.refresh() - for r in self.routes: - try: - netmask = int(netmask) - except: - netmask = ipcalc.Network(0, netmask).mask - ip = '%s/%d' % (ip, netmask) - if r.dst == ip: - return r - + dst = self._dstTranslate(ip, netmask) + with self.lock: + self.refresh() + for r in self.routes: + if r.dst == dst: + return r return None def getRoutes(self): @@ -237,6 +229,25 @@ self.refresh() return self.routes[:] + def _dstTranslate(self, ip, netmask=None): + """ Linux's routing/libnl-route system is a bit funny when it comes + to default routes. We can send them a dst of 0.0.0.0/0 and the kernel will + mangle that into an empty addr when we look at the route again. This + makes it difficult to do things like find a route that you just + added. """ + + if netmask: + netmask = ipcalc.Network(0, netmask).mask + ip = '%s/%d' % (ip, netmask) + + if ip not in ('default', 'any', 'all'): + n = ipcalc.Network(ip) + if not n.ip + n.mask: + ip = '0.0.0.0' + else: + ip = '0.0.0.0' + + return ip class Rule(RuleBase): diff -r 1c5d2a037d54 service_manager/services/racoon.py --- a/service_manager/services/racoon.py Wed Aug 15 17:43:52 2012 -0600 +++ b/service_manager/services/racoon.py Thu Aug 16 18:36:32 2012 -0600 @@ -44,11 +44,10 @@ serialize = serializ.Serializable.serialize -# NOTE, this is documented using the sphinx syntax: http://sphinx.pocoo.org/rest.html class Racoon(serializ.Serializable, services.Service): """Racoon, Internet Key Exchange (IKE) daemon for automatically keying IPsec connections - `IPsec tools` `_ - """ + IPsec tools """ + name = 'racoon' __startup__ = 75 @@ -106,10 +105,18 @@ self.enableRacoon(None, self.cStore.get('config.vpn.enabled', eventing=False), booting=True) - def onStop(self): - - self.freeRules() - + @serialize + def wait(self, seconds): + return self.wait2(seconds) + + @serialize + def wait2(self, x): + print("2START") + import time + time.sleep(seconds) + print("2DONE") + + @serialize def __startTunnel(self, path, cfg): @@ -277,16 +284,27 @@ def start(self): """ Update the config files and start the racoon daemon. """ + + import threading + t = str(threading.currentThread()) + logger.info("STARTING RACOON: %s" % t) + + if not self.enabled: + logger.warning("NOT ENABELD") return if not self.updateConfig(): logger.warning("No suitable interface found. VPN service not running.") self.running = False + logger.warning("NOT SUITABLE ENABELD") return + logger.info("STEP 1: %s" % t) self.addRules() + logger.info("STEP 2: %s" % t) self.addRoutes() + logger.info("STEP 3: %s" % t) cmdArgs = ['-f', PATH_IPSEC, PATH_CONFIG_FILE] cmd = self._racoon_cmd @@ -298,8 +316,11 @@ #subprocess.call(["/sbin/sysctl", "-q", "-w", "net.inet.ipsec.active=1"]) + logger.info("STEP 4: %s" % t) self.procMon.setCmd(cmd) + logger.info("STEP 5: %s" % t) self.procMon.start() + logger.info("STEP 6: %s" % t) self.resolveGatewaysId = self.loopedTask.add(self.resolveGateways, self.resolveGatewaysTimer) @@ -308,10 +329,14 @@ self.running = True + logger.info("STEP 7: %s" % t) + logger.info("STARTED RACOON") + @serialize def stop(self): """ Stop a running racoon daemon. """ + logger.info("STOPING RACOON") self.running = False vpn_config = self.cStore.get('config.vpn', eventing=False) or {} adv_config = vpn_config.get('adv_settings', {}) @@ -328,15 +353,18 @@ os.system("setkey -F; setkey -FP") self.procMon.stop() + logger.info("IS STOPPED RACOON") @serialize def refresh(self, *na): + logger.info("Doing REFRESH") if self.running: self.stop() if self.enabled and self.wanmgr.findDevices(state='connected'): self.start() + logger.info("DONE REFRESH") def addRoutes(self): """ This will allow traffic originating from the router to go through the tunnel if the @@ -345,14 +373,20 @@ We also add an explicit route for the remote gateway so we can force tunnel binding out a particular wan when WAN binding is used. """ + import threading + t = str(threading.currentThread()) + logger.info("R STEP 1: %s" % t) for tun in self.config_map.values(): + logger.info("R STEP 2: %s" % t) local_nets = [ ipcalc.Network(x['ip_address'], x['netmask']) for x in tun['local_network'] ] local_router_ips = [ (x.ip_address in xx and x.ip_address) \ for x in self.lanmgr.networks \ for xx in local_nets ] if not any(local_router_ips): + logger.info("R STEP 2:b %s" % t) continue + logger.info("R STEP 3 %s" % t) for r in tun['remote_network']: ip = r.get('ip_address') @@ -360,13 +394,20 @@ gw = local_router_ips[0] if ip != '0.0.0.0': try: + logger.info("R STEP 4: %s" % t) self.routeTable.addRoute(ip=ip, netmask=netmask, gw=gw) + logger.info("R STEP 4b: %s" % t) except Exception as e: + logger.info("R STEP 4c: %s" % t) logger.warn("Route IP: %s Mask: %s GW: %s could not be added to table. %s", ip, netmask, gw, e) + logger.info("R STEP 5: %s" % t) logger.info("Adding route IP:%s Mask:%s GW:%s", ip, netmask, gw) + logger.info("R STEP 6: %s" % t) self.routeTable.addRoute(ip=tun['remote_gw_resolved'], gw=tun['wans'][0].ip_address) + logger.info("R STEP 6b: %s" % t) + logger.info("R STEP 7: %s" % t) def removeRoutes(self): @@ -378,21 +419,29 @@ self.gw_set = xtables.IPSet('gw_ipsec', 'hash:ip') self.net_set = xtables.IPSet('net_ipsec', 'hash:net') - """ Allow remote uses access to local LAN services. """ + # Allow remote user access to local LAN services. with nf.getChain('filter', 'LAN_SERVERS') as chain: self.lan_hook = chain.addRule(nf.AcceptRule(match=nf.SetMatch('src', 'net_ipsec'))) with nf.getChain('filter', 'WAN_SERVERS') as chain: self.esp_hook = chain.addRule(nf.AcceptRule(protocol=socket.IPPROTO_ESP, - match=nf.SetMatch('src', 'gw_ipsec'))) + match=nf.SetMatch('src', 'gw_ipsec'))) + self.anon_esp_hook = chain.addRule(nf.AcceptRule(protocol=socket.IPPROTO_ESP, + enabled=False)) self.ike_hook = chain.addRule(nf.AcceptRule(protocol=socket.IPPROTO_UDP, - match=nf.SetMatch('src', 'gw_ipsec'), - triggers=IKEPortTrigger)) + match=nf.SetMatch('src', 'gw_ipsec'), + triggers=IKEPortTrigger)) + self.anon_ike_hook = chain.addRule(nf.AcceptRule(protocol=socket.IPPROTO_UDP, + triggers=IKEPortTrigger, + enabled=False)) self.natt_hook = chain.addRule(nf.AcceptRule(protocol=socket.IPPROTO_UDP, - match=nf.SetMatch('src', 'gw_ipsec'), - triggers=NATTPortTrigger)) + match=nf.SetMatch('src', 'gw_ipsec'), + triggers=NATTPortTrigger)) + self.anon_natt_hook = chain.addRule(nf.AcceptRule(protocol=socket.IPPROTO_UDP, + triggers=NATTPortTrigger, + enabled=False)) self.in_hook = nf.getChain('filter', 'FORWARD').addRule({ 'priority': 1, @@ -416,53 +465,34 @@ def addRules(self): - if self.anonymousCheck(): - with nf.getChain('filter', 'WAN_SERVERS') as chain: - chain.removeRule(self.esp_hook) - chain.removeRule(self.ike_hook) - chain.removeRule(self.natt_hook) - self.esp_hook = chain.addRule(nf.AcceptRule(protocol=socket.IPPROTO_ESP)) - self.ike_hook = chain.addRule(nf.AcceptRule(protocol=socket.IPPROTO_UDP, triggers=IKEPortTrigger)) - self.natt_hook = chain.addRule(nf.AcceptRule(protocol=socket.IPPROTO_UDP, triggers=NATTPortTrigger)) + with nf.getChain('filter', 'WAN_SERVERS') as chain: + enabler = self.anonymousCheck() + self.anon_esp_hook.enabled = enabler + self.anon_ike_hook.enabled = enabler + self.anon_natt_hook.enabled = enabler for x_tunnel in self.config_map.values(): - if x_tunnel['remote_gateway'] != '0.0.0.0': + if x_tunnel['remote_gateway'] != '0.0.0.0' and x_tunnel['remote_gateway'] not in self.gw_set: self.gw_set.append(x_tunnel['remote_gateway']) logger.info("Allowing inbound ESP %s host" % x_tunnel['remote_gateway']) for r in x_tunnel['remote_network']: mask = ipcalc.Network(r['ip_address'], r['netmask']).mask - if r['ip_address'] != '0.0.0.0' and mask != 0: - self.net_set.append('%s/%s' % (r['ip_address'], mask)) - logger.info("Allowing inbound tunnel network %s/%s" % (r['ip_address'], mask)) + net = '%s/%s' % (r['ip_address'], mask) + if r['ip_address'] != '0.0.0.0' and mask != 0 and net not in self.net_set: + self.net_set.append(net) + logger.info("Allowing inbound tunnel network %s" % net) def removeRules(self): - for x_tunnel in self.config_map.values(): + with nf.getChain('filter', 'WAN_SERVERS') as chain: + self.anon_esp_hook.enabled = False + self.anon_ike_hook.enabled = False + self.anon_natt_hook.enabled = False - if x_tunnel['remote_gateway'] != '0.0.0.0': - self.gw_set.remove(x_tunnel['remote_gateway']) - logger.info("Denying inbound ESP %s host" % x_tunnel['remote_gateway']) - - for r in x_tunnel['remote_network']: - mask = ipcalc.Network(r['ip_address'], r['netmask']).mask - if r['ip_address'] != '0.0.0.0' and mask != 0: - self.net_set.remove('%s/%s' % (r['ip_address'], mask)) - logger.info("Removing inbound tunnel network %s/%s" % (r['ip_address'], mask)) - - def freeRules(self): - - with nf.getChain('filter', 'LAN_SERVERS') as chain: - chain.remoteRule(self.lan_hook) - - with nf.getChain('filter', 'WAN_SERVERS') as chain: - chain.removeRule(self.esp_hook) - chain.removeRule(self.ike_hook) - chain.removeRule(self.natt_hook) - nf.getChain('filter', 'FOWARD').removeRule(self.in_hook) - nf.getChain('nat', 'POSTROUTING').removeRule(self.out_hook) - self.gw_set.destroy() + self.gw_set.flush() + self.net_set.flush() def inferIdent(self, ident):