X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=netlink.c;h=b7cdb4cdef4a4ca6496f18b2ffa0cd08f3900f57;hb=8dea8d37a13fcc615daba3375809900f04a2e5a2;hp=e09eacf95c4466015bf393eaf1fa75cdcd634073;hpb=7138d0c54cd2212439434d27cb2d6ea775c3039b;p=secnet.git diff --git a/netlink.c b/netlink.c index e09eacf..b7cdb4c 100644 --- a/netlink.c +++ b/netlink.c @@ -12,6 +12,7 @@ #include "util.h" #include "ipaddr.h" #include "netlink.h" +#include "process.h" /* Generic IP checksum routine */ static inline uint16_t ip_csum(uint8_t *iph,uint32_t count) @@ -176,11 +177,19 @@ static void netlink_icmp_csum(struct icmphdr *h) static bool_t netlink_icmp_may_reply(struct buffer_if *buf) { struct iphdr *iph; + struct icmphdr *icmph; uint32_t source; iph=(struct iphdr *)buf->start; - if (iph->protocol==1) return False; /* Overly-broad; we may reply to - eg. icmp echo-request */ + icmph=(struct icmphdr *)buf->start; + if (iph->protocol==1) { + switch(icmph->type) { + case 3: /* Destination unreachable */ + case 11: /* Time Exceeded */ + case 12: /* Parameter Problem */ + return False; + } + } /* How do we spot broadcast destination addresses? */ if (ntohs(iph->frag_off)&0x1fff) return False; /* Non-initial fragment */ source=ntohl(iph->saddr); @@ -434,8 +443,8 @@ static void netlink_incoming(void *sst, void *cid, struct buffer_if *buf) /* Check source */ if (client) { - /* Check that the packet source is in 'nets' and its destination is - in st->networks */ + /* Check that the packet source is appropriate for the tunnel + it came down */ if (!subnet_matches_list(client->networks,source)) { string_t s,d; s=ipaddr_to_string(source); @@ -447,6 +456,9 @@ static void netlink_incoming(void *sst, void *cid, struct buffer_if *buf) return; } } else { + /* Check that the packet originates in our configured local + network, and hasn't been forwarded from elsewhere or + generated with the wrong source address */ if (!subnet_matches_list(&st->networks,source)) { string_t s,d; s=ipaddr_to_string(source); @@ -458,6 +470,20 @@ static void netlink_incoming(void *sst, void *cid, struct buffer_if *buf) return; } } + + /* If this is a point-to-point device we don't examine the packet at + all; we blindly send it down our one-and-only registered tunnel, + or to the host, depending on where it came from. */ + if (st->ptp) { + if (client) { + st->deliver_to_host(st->dst,NULL,buf); + } else { + st->clients->deliver(st->clients->dst,NULL,buf); + } + BUF_ASSERT_FREE(buf); + return; + } + /* (st->secnet_address needs checking before matching destination addresses) */ if (dest==st->secnet_address) { @@ -484,15 +510,18 @@ static void netlink_incoming(void *sst, void *cid, struct buffer_if *buf) } static void netlink_set_softlinks(struct netlink *st, struct netlink_client *c, - bool_t up) + bool_t up, uint32_t quality) { uint32_t i; if (!st->routes) return; /* Table has not yet been created */ for (i=0; in_routes; i++) { - if (!st->routes[i].hard && st->routes[i].c==c) { - st->routes[i].up=up; - st->set_route(st->dst,&st->routes[i]); + if (st->routes[i].c==c) { + st->routes[i].quality=quality; + if (!st->routes[i].hard) { + st->routes[i].up=up; + st->set_route(st->dst,&st->routes[i]); + } } } } @@ -504,9 +533,9 @@ static void netlink_set_quality(void *sst, void *cid, uint32_t quality) c->link_quality=quality; if (c->link_quality==LINK_QUALITY_DOWN) { - netlink_set_softlinks(st,c,False); + netlink_set_softlinks(st,c,False,c->link_quality); } else { - netlink_set_softlinks(st,c,True); + netlink_set_softlinks(st,c,True,c->link_quality); } } @@ -541,14 +570,20 @@ static void *netlink_regnets(void *sst, struct subnet_list *nets, Message(M_ERROR,"%s: site %s specifies networks that " "intersect with the explicitly excluded remote networks\n", st->name,client_name); - return False; + return NULL; + } + + if (st->clients && st->ptp) { + fatal("%s: only one site may use a point-to-point netlink device\n", + st->name); + return NULL; } c=safe_malloc(sizeof(*c),"netlink_regnets"); c->networks=nets; c->deliver=deliver; c->dst=dst; - c->name=client_name; /* XXX copy it? */ + c->name=client_name; c->options=options; c->link_quality=LINK_QUALITY_DOWN; c->next=st->clients; @@ -560,26 +595,29 @@ static void *netlink_regnets(void *sst, struct subnet_list *nets, return c; } -static void netlink_dump_routes(struct netlink *st) +static void netlink_dump_routes(struct netlink *st, bool_t requested) { int i; string_t net; + uint32_t c=M_INFO; - Message(M_INFO,"%s: routing table:\n",st->name); + if (requested) c=M_WARNING; + Message(c,"%s: routing table:\n",st->name); for (i=0; in_routes; i++) { net=subnet_to_string(&st->routes[i].net); - Message(M_INFO,"%s -> tunnel %s (%s,%s route,%s)\n",net, + Message(c,"%s -> tunnel %s (%s,%s route,%s,quality %d)\n",net, st->routes[i].c->name, st->routes[i].hard?"hard":"soft", st->routes[i].allow_route?"free":"restricted", - st->routes[i].up?"up":"down"); + st->routes[i].up?"up":"down", + st->routes[i].quality); free(net); } - Message(M_INFO,"%s/32 -> netlink \"%s\"\n", + Message(c,"%s/32 -> netlink \"%s\"\n", ipaddr_to_string(st->secnet_address),st->name); for (i=0; inetworks.entries; i++) { net=subnet_to_string(&st->networks.list[i]); - Message(M_INFO,"%s -> host\n",net); + Message(c,"%s -> host\n",net); free(net); } } @@ -600,6 +638,13 @@ static void netlink_phase_hook(void *sst, uint32_t new_phase) struct netlink_client *c; uint32_t i,j; + if (!st->clients && st->ptp) { + /* Point-to-point netlink devices must have precisely one + client. If none has registered by now, complain. */ + fatal("%s: point-to-point netlink devices must have precisely " + "one client. This one doesn't have any.\n",st->name); + } + /* All the networks serviced by the various tunnels should now * have been registered. We build a routing table by sorting the * routes into most-specific-first order. */ @@ -618,6 +663,7 @@ static void netlink_phase_hook(void *sst, uint32_t new_phase) st->routes[i].hard=c->options&NETLINK_OPTION_SOFTROUTE?False:True; st->routes[i].allow_route=c->options&NETLINK_OPTION_ALLOW_ROUTE? True:False; + st->routes[i].quality=c->link_quality; i++; } } @@ -630,7 +676,14 @@ static void netlink_phase_hook(void *sst, uint32_t new_phase) qsort(st->routes,st->n_routes,sizeof(*st->routes), netlink_compare_route_specificity); - netlink_dump_routes(st); + netlink_dump_routes(st,False); +} + +static void netlink_signal_handler(void *sst, int signum) +{ + struct netlink *st=sst; + Message(M_INFO,"%s: route dump requested by SIGUSR1\n",st->name); + netlink_dump_routes(st,True); } netlink_deliver_fn *netlink_init(struct netlink *st, @@ -639,6 +692,8 @@ netlink_deliver_fn *netlink_init(struct netlink *st, netlink_route_fn *set_route, netlink_deliver_fn *to_host) { + item_t *sa, *ptpa; + st->dst=dst; st->cl.description=description; st->cl.type=CL_NETLINK; @@ -663,14 +718,30 @@ netlink_deliver_fn *netlink_init(struct netlink *st, /* secnet-address does not have to be in local-networks; however, it should be advertised in the 'sites' file for the local site. */ - st->secnet_address=string_to_ipaddr( - dict_find_item(dict,"secnet-address", True, "netlink", loc),"netlink"); + sa=dict_find_item(dict,"secnet-address",False,"netlink",loc); + ptpa=dict_find_item(dict,"ptp-address", False, "netlink", loc); + if (sa && ptpa) { + cfgfatal(loc,st->name,"you may not specify secnet-address and " + "ptp-address in the same netlink device\n"); + } + if (!(sa || ptpa)) { + cfgfatal(loc,st->name,"you must specify secnet-address or " + "ptp-address for this netlink device\n"); + } + if (sa) { + st->secnet_address=string_to_ipaddr(sa,"netlink"); + st->ptp=False; + } else { + st->secnet_address=string_to_ipaddr(ptpa,"netlink"); + st->ptp=True; + } st->mtu=dict_read_number(dict, "mtu", False, "netlink", loc, DEFAULT_MTU); buffer_new(&st->icmp,ICMP_BUFSIZE); st->n_routes=0; st->routes=NULL; add_hook(PHASE_SETUP,netlink_phase_hook,st); + request_signal_notification(SIGUSR1, netlink_signal_handler, st); return netlink_incoming; }