chiark / gitweb /
Import release 0.1.7
[secnet.git] / netlink.c
1 /* User-kernel network link */
2
3 /* Each netlink device is actually a router, with its own IP address.
4    We do things like decreasing the TTL and recalculating the header
5    checksum, generating ICMP, responding to pings, etc. */
6
7 /* This is where we have the anti-spoofing paranoia - before sending a
8    packet to the kernel we check that the tunnel it came over could
9    reasonably have produced it. */
10
11 /* XXX new feature: "point-to-point" mode.  Instead of specifying a
12    secnet-address in the configuration dictionary, the user specifies
13    the address of the machine at the other end of the (one and only)
14    tunnel.  We bypass all IP packet processing code.  This mode is
15    useful for leafnodes like laptops, which don't require a secnet
16    router address. */
17
18 #include "secnet.h"
19 #include "util.h"
20 #include "ipaddr.h"
21 #include "netlink.h"
22 #include "process.h"
23
24 /* Generic IP checksum routine */
25 static inline uint16_t ip_csum(uint8_t *iph,uint32_t count)
26 {
27     register uint32_t sum=0;
28
29     while (count>1) {
30         sum+=ntohs(*(uint16_t *)iph);
31         iph+=2;
32         count-=2;
33     }
34     if(count>0)
35         sum+=*(uint8_t *)iph;
36     while (sum>>16)
37         sum=(sum&0xffff)+(sum>>16);
38     return htons(~sum);
39 }
40
41 #ifdef i386
42 /*
43  *      This is a version of ip_compute_csum() optimized for IP headers,
44  *      which always checksum on 4 octet boundaries.
45  *
46  *      By Jorge Cwik <jorge@laser.satlink.net>, adapted for linux by
47  *      Arnt Gulbrandsen.
48  */
49 static inline uint16_t ip_fast_csum(uint8_t *iph, uint32_t ihl) {
50     uint32_t sum;
51
52     __asm__ __volatile__("
53             movl (%1), %0
54             subl $4, %2
55             jbe 2f
56             addl 4(%1), %0
57             adcl 8(%1), %0
58             adcl 12(%1), %0
59 1:          adcl 16(%1), %0
60             lea 4(%1), %1
61             decl %2
62             jne 1b
63             adcl $0, %0
64             movl %0, %2
65             shrl $16, %0
66             addw %w2, %w0
67             adcl $0, %0
68             notl %0
69 2:
70             "
71         /* Since the input registers which are loaded with iph and ipl
72            are modified, we must also specify them as outputs, or gcc
73            will assume they contain their original values. */
74         : "=r" (sum), "=r" (iph), "=r" (ihl)
75         : "1" (iph), "2" (ihl));
76     return sum;
77 }
78 #else
79 static inline uint16_t ip_fast_csum(uint8_t *iph, uint32_t ihl)
80 {
81     return ip_csum(iph,ihl*4);
82 }
83 #endif
84
85 struct iphdr {
86 #if defined (WORDS_BIGENDIAN)
87     uint8_t    version:4,
88                ihl:4;
89 #else
90     uint8_t    ihl:4,
91                version:4;
92 #endif
93     uint8_t    tos;
94     uint16_t   tot_len;
95     uint16_t   id;
96     uint16_t   frag_off;
97     uint8_t    ttl;
98     uint8_t    protocol;
99     uint16_t   check;
100     uint32_t   saddr;
101     uint32_t   daddr;
102     /* The options start here. */
103 };
104
105 struct icmphdr {
106     struct iphdr iph;
107     uint8_t type;
108     uint8_t code;
109     uint16_t check;
110     union {
111         uint32_t unused;
112         struct {
113             uint8_t pointer;
114             uint8_t unused1;
115             uint16_t unused2;
116         } pprob;
117         uint32_t gwaddr;
118         struct {
119             uint16_t id;
120             uint16_t seq;
121         } echo;
122     } d;
123 };
124     
125 static void netlink_packet_deliver(struct netlink *st,
126                                    struct netlink_client *client,
127                                    struct buffer_if *buf);
128
129 static struct icmphdr *netlink_icmp_tmpl(struct netlink *st,
130                                          uint32_t dest,uint16_t len)
131 {
132     struct icmphdr *h;
133
134     BUF_ALLOC(&st->icmp,"netlink_icmp_tmpl");
135     buffer_init(&st->icmp,st->max_start_pad);
136     h=buf_append(&st->icmp,sizeof(*h));
137
138     h->iph.version=4;
139     h->iph.ihl=5;
140     h->iph.tos=0;
141     h->iph.tot_len=htons(len+(h->iph.ihl*4)+8);
142     h->iph.id=0;
143     h->iph.frag_off=0;
144     h->iph.ttl=255;
145     h->iph.protocol=1;
146     h->iph.saddr=htonl(st->secnet_address);
147     h->iph.daddr=htonl(dest);
148     h->iph.check=0;
149     h->iph.check=ip_fast_csum((uint8_t *)&h->iph,h->iph.ihl);
150     h->check=0;
151     h->d.unused=0;
152
153     return h;
154 }
155
156 /* Fill in the ICMP checksum field correctly */
157 static void netlink_icmp_csum(struct icmphdr *h)
158 {
159     uint32_t len;
160
161     len=ntohs(h->iph.tot_len)-(4*h->iph.ihl);
162     h->check=0;
163     h->check=ip_csum(&h->type,len);
164 }
165
166 /* RFC1122:
167  *       An ICMP error message MUST NOT be sent as the result of
168  *       receiving:
169  *
170  *       *    an ICMP error message, or
171  *
172  *       *    a datagram destined to an IP broadcast or IP multicast
173  *            address, or
174  *
175  *       *    a datagram sent as a link-layer broadcast, or
176  *
177  *       *    a non-initial fragment, or
178  *
179  *       *    a datagram whose source address does not define a single
180  *            host -- e.g., a zero address, a loopback address, a
181  *            broadcast address, a multicast address, or a Class E
182  *            address.
183  */
184 static bool_t netlink_icmp_may_reply(struct buffer_if *buf)
185 {
186     struct iphdr *iph;
187     uint32_t source;
188
189     iph=(struct iphdr *)buf->start;
190     if (iph->protocol==1) return False; /* Overly-broad; we may reply to
191                                            eg. icmp echo-request */
192     /* How do we spot broadcast destination addresses? */
193     if (ntohs(iph->frag_off)&0x1fff) return False; /* Non-initial fragment */
194     source=ntohl(iph->saddr);
195     if (source==0) return False;
196     if ((source&0xff000000)==0x7f000000) return False;
197     /* How do we spot broadcast source addresses? */
198     if ((source&0xf0000000)==0xe0000000) return False; /* Multicast */
199     if ((source&0xf0000000)==0xf0000000) return False; /* Class E */
200     return True;
201 }
202
203 /* How much of the original IP packet do we include in its ICMP
204    response? The header plus up to 64 bits. */
205 static uint16_t netlink_icmp_reply_len(struct buffer_if *buf)
206 {
207     struct iphdr *iph=(struct iphdr *)buf->start;
208     uint16_t hlen,plen;
209
210     hlen=iph->ihl*4;
211     /* We include the first 8 bytes of the packet data, provided they exist */
212     hlen+=8;
213     plen=ntohs(iph->tot_len);
214     return (hlen>plen?plen:hlen);
215 }
216
217 /* client indicates where the packet we're constructing a response to
218    comes from. NULL indicates the host. */
219 static void netlink_icmp_simple(struct netlink *st, struct buffer_if *buf,
220                                 struct netlink_client *client,
221                                 uint8_t type, uint8_t code)
222 {
223     struct iphdr *iph=(struct iphdr *)buf->start;
224     struct icmphdr *h;
225     uint16_t len;
226
227     if (netlink_icmp_may_reply(buf)) {
228         len=netlink_icmp_reply_len(buf);
229         h=netlink_icmp_tmpl(st,ntohl(iph->saddr),len);
230         h->type=type; h->code=code;
231         memcpy(buf_append(&st->icmp,len),buf->start,len);
232         netlink_icmp_csum(h);
233         netlink_packet_deliver(st,NULL,&st->icmp);
234         BUF_ASSERT_FREE(&st->icmp);
235     }
236 }
237
238 /*
239  * RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the
240  * checksum.
241  *
242  * Is the datagram acceptable?
243  *
244  * 1. Length at least the size of an ip header
245  * 2. Version of 4
246  * 3. Checksums correctly.
247  * 4. Doesn't have a bogus length
248  */
249 static bool_t netlink_check(struct netlink *st, struct buffer_if *buf)
250 {
251     struct iphdr *iph=(struct iphdr *)buf->start;
252     uint32_t len;
253
254     if (iph->ihl < 5 || iph->version != 4) return False;
255     if (buf->size < iph->ihl*4) return False;
256     if (ip_fast_csum((uint8_t *)iph, iph->ihl)!=0) return False;
257     len=ntohs(iph->tot_len);
258     /* There should be no padding */
259     if (buf->size!=len || len<(iph->ihl<<2)) return False;
260     /* XXX check that there's no source route specified */
261     return True;
262 }
263
264 /* Deliver a packet. "client" points to the _origin_ of the packet, not
265    its destination. (May be used when sending ICMP response - avoid
266    asymmetric routing.) */
267 static void netlink_packet_deliver(struct netlink *st,
268                                    struct netlink_client *client,
269                                    struct buffer_if *buf)
270 {
271     struct iphdr *iph=(struct iphdr *)buf->start;
272     uint32_t dest=ntohl(iph->daddr);
273     uint32_t source=ntohl(iph->saddr);
274     uint32_t best_quality;
275     int best_match;
276     int i;
277
278     BUF_ASSERT_USED(buf);
279
280     if (dest==st->secnet_address) {
281         Message(M_ERROR,"%s: trying to deliver a packet to myself!\n");
282         BUF_FREE(buf);
283         return;
284     }
285     
286     /* XXX we're going to need an extra value 'allow_route' for the
287        source of the packet. It's always True for packets from the
288        host. For packets from tunnels, we consult the client
289        options. If !allow_route and the destination is a tunnel that
290        also doesn't allow routing, we must reject the packet with an
291        'administratively prohibited' or something similar ICMP. */
292     if (!client) {
293         /* Origin of packet is host or secnet. Might be for a tunnel. */
294         best_quality=0;
295         best_match=-1;
296         for (i=0; i<st->n_routes; i++) {
297             if (st->routes[i].up && subnet_match(&st->routes[i].net,dest)) {
298                 if (st->routes[i].c->link_quality>best_quality
299                     || best_quality==0) {
300                     best_quality=st->routes[i].c->link_quality;
301                     best_match=i;
302                     /* If quality isn't perfect we may wish to
303                        consider kicking the tunnel with a 0-length
304                        packet to prompt it to perform a key setup.
305                        Then it'll eventually decide it's up or
306                        down. */
307                     /* If quality is perfect we don't need to search
308                        any more. */
309                     if (best_quality>=MAXIMUM_LINK_QUALITY) break;
310                 }
311             }
312         }
313         if (best_match==-1) {
314             /* Not going down a tunnel. Might be for the host. 
315                XXX think about this - only situation should be if we're
316                sending ICMP. */
317             if (source!=st->secnet_address) {
318                 Message(M_ERROR,"netlink_packet_deliver: outgoing packet "
319                         "from host that won't fit down any of our tunnels!\n");
320                 /* XXX I think this could also occur if a soft tunnel just
321                    went down, but still had packets queued in the kernel. */
322                 BUF_FREE(buf);
323             } else {
324                 st->deliver_to_host(st->dst,NULL,buf);
325                 BUF_ASSERT_FREE(buf);
326             }
327         } else {
328             if (best_quality>0) {
329                 st->routes[best_match].c->deliver(
330                     st->routes[best_match].c->dst,
331                     st->routes[best_match].c, buf);
332                 BUF_ASSERT_FREE(buf);
333             } else {
334                 /* Generate ICMP destination unreachable */
335                 netlink_icmp_simple(st,buf,client,3,0); /* client==NULL */
336                 BUF_FREE(buf);
337             }
338         }
339     } else { /* client is set */
340         /* We know the origin is a tunnel - packet must be for the host */
341         /* XXX THIS IS NOT NECESSARILY TRUE, AND NEEDS FIXING */
342         /* THIS FUNCTION MUST JUST DELIVER THE PACKET: IT MUST ASSUME
343            THE PACKET HAS ALREADY BEEN CHECKED */
344         if (subnet_matches_list(&st->networks,dest)) {
345             st->deliver_to_host(st->dst,NULL,buf);
346             BUF_ASSERT_FREE(buf);
347         } else {
348             Message(M_ERROR,"%s: packet from tunnel %s can't be delivered "
349                     "to the host\n",st->name,client->name);
350             netlink_icmp_simple(st,buf,client,3,0);
351             BUF_FREE(buf);
352         }
353     }
354     BUF_ASSERT_FREE(buf);
355 }
356
357 static void netlink_packet_forward(struct netlink *st, 
358                                    struct netlink_client *client,
359                                    struct buffer_if *buf)
360 {
361     struct iphdr *iph=(struct iphdr *)buf->start;
362     
363     BUF_ASSERT_USED(buf);
364
365     /* Packet has already been checked */
366     if (iph->ttl<=1) {
367         /* Generate ICMP time exceeded */
368         netlink_icmp_simple(st,buf,client,11,0);
369         BUF_FREE(buf);
370         return;
371     }
372     iph->ttl--;
373     iph->check=0;
374     iph->check=ip_fast_csum((uint8_t *)iph,iph->ihl);
375
376     netlink_packet_deliver(st,client,buf);
377     BUF_ASSERT_FREE(buf);
378 }
379
380 /* Deal with packets addressed explicitly to us */
381 static void netlink_packet_local(struct netlink *st,
382                                  struct netlink_client *client,
383                                  struct buffer_if *buf)
384 {
385     struct icmphdr *h;
386
387     h=(struct icmphdr *)buf->start;
388
389     if ((ntohs(h->iph.frag_off)&0xbfff)!=0) {
390         Message(M_WARNING,"%s: fragmented packet addressed to secnet; "
391                 "ignoring it\n",st->name);
392         BUF_FREE(buf);
393         return;
394     }
395
396     if (h->iph.protocol==1) {
397         /* It's ICMP */
398         if (h->type==8 && h->code==0) {
399             /* ICMP echo-request. Special case: we re-use the buffer
400                to construct the reply. */
401             h->type=0;
402             h->iph.daddr=h->iph.saddr;
403             h->iph.saddr=htonl(st->secnet_address);
404             h->iph.ttl=255; /* Be nice and bump it up again... */
405             h->iph.check=0;
406             h->iph.check=ip_fast_csum((uint8_t *)h,h->iph.ihl);
407             netlink_icmp_csum(h);
408             netlink_packet_deliver(st,NULL,buf);
409             return;
410         }
411         Message(M_WARNING,"%s: unknown incoming ICMP\n",st->name);
412     } else {
413         /* Send ICMP protocol unreachable */
414         netlink_icmp_simple(st,buf,client,3,2);
415         BUF_FREE(buf);
416         return;
417     }
418
419     BUF_FREE(buf);
420 }
421
422 /* If cid==NULL packet is from host, otherwise cid specifies which tunnel 
423    it came from. */
424 static void netlink_incoming(void *sst, void *cid, struct buffer_if *buf)
425 {
426     struct netlink *st=sst;
427     struct netlink_client *client=cid;
428     uint32_t source,dest;
429     struct iphdr *iph;
430
431     BUF_ASSERT_USED(buf);
432     if (!netlink_check(st,buf)) {
433         Message(M_WARNING,"%s: bad IP packet from %s\n",
434                 st->name,client?client->name:"host");
435         BUF_FREE(buf);
436         return;
437     }
438     iph=(struct iphdr *)buf->start;
439
440     source=ntohl(iph->saddr);
441     dest=ntohl(iph->daddr);
442
443     /* Check source */
444     if (client) {
445         /* Check that the packet source is in 'nets' and its destination is
446            in st->networks */
447         if (!subnet_matches_list(client->networks,source)) {
448             string_t s,d;
449             s=ipaddr_to_string(source);
450             d=ipaddr_to_string(dest);
451             Message(M_WARNING,"%s: packet from tunnel %s with bad "
452                     "source address (s=%s,d=%s)\n",st->name,client->name,s,d);
453             free(s); free(d);
454             BUF_FREE(buf);
455             return;
456         }
457     } else {
458         if (!subnet_matches_list(&st->networks,source)) {
459             string_t s,d;
460             s=ipaddr_to_string(source);
461             d=ipaddr_to_string(dest);
462             Message(M_WARNING,"%s: outgoing packet with bad source address "
463                     "(s=%s,d=%s)\n",st->name,s,d);
464             free(s); free(d);
465             BUF_FREE(buf);
466             return;
467         }
468     }
469     /* (st->secnet_address needs checking before matching destination
470        addresses) */
471     if (dest==st->secnet_address) {
472         netlink_packet_local(st,client,buf);
473         BUF_ASSERT_FREE(buf);
474         return;
475     }
476     if (client) {
477         /* Check for free routing */
478         if (!subnet_matches_list(&st->networks,dest)) {
479             string_t s,d;
480             s=ipaddr_to_string(source);
481             d=ipaddr_to_string(dest);
482             Message(M_WARNING,"%s: incoming packet from tunnel %s "
483                     "with bad destination address "
484                     "(s=%s,d=%s)\n",st->name,client->name,s,d);
485             free(s); free(d);
486             BUF_FREE(buf);
487             return;
488         }
489     }
490     netlink_packet_forward(st,client,buf);
491     BUF_ASSERT_FREE(buf);
492 }
493
494 static void netlink_set_softlinks(struct netlink *st, struct netlink_client *c,
495                                   bool_t up, uint32_t quality)
496 {
497     uint32_t i;
498
499     if (!st->routes) return; /* Table has not yet been created */
500     for (i=0; i<st->n_routes; i++) {
501         if (st->routes[i].c==c) {
502             st->routes[i].quality=quality;
503             if (!st->routes[i].hard) {
504                 st->routes[i].up=up;
505                 st->set_route(st->dst,&st->routes[i]);
506             }
507         }
508     }
509 }
510
511 static void netlink_set_quality(void *sst, void *cid, uint32_t quality)
512 {
513     struct netlink *st=sst;
514     struct netlink_client *c=cid;
515
516     c->link_quality=quality;
517     if (c->link_quality==LINK_QUALITY_DOWN) {
518         netlink_set_softlinks(st,c,False,c->link_quality);
519     } else {
520         netlink_set_softlinks(st,c,True,c->link_quality);
521     }
522 }
523
524 static void *netlink_regnets(void *sst, struct subnet_list *nets,
525                              netlink_deliver_fn *deliver, void *dst,
526                              uint32_t max_start_pad, uint32_t max_end_pad,
527                              uint32_t options, string_t client_name)
528 {
529     struct netlink *st=sst;
530     struct netlink_client *c;
531
532     Message(M_DEBUG_CONFIG,"netlink_regnets: request for %d networks, "
533             "max_start_pad=%d, max_end_pad=%d\n",
534             nets->entries,max_start_pad,max_end_pad);
535
536     if ((options&NETLINK_OPTION_SOFTROUTE) && !st->set_route) {
537         Message(M_ERROR,"%s: this netlink device does not support "
538                 "soft routes.\n");
539         return NULL;
540     }
541
542     if (options&NETLINK_OPTION_SOFTROUTE) {
543         /* XXX for now we assume that soft routes require root privilege;
544            this may not always be true. The device driver can tell us. */
545         require_root_privileges=True;
546         require_root_privileges_explanation="netlink: soft routes";
547     }
548
549     /* Check that nets do not intersect st->exclude_remote_networks;
550        refuse to register if they do. */
551     if (subnet_lists_intersect(&st->exclude_remote_networks,nets)) {
552         Message(M_ERROR,"%s: site %s specifies networks that "
553                 "intersect with the explicitly excluded remote networks\n",
554                 st->name,client_name);
555         return False;
556     }
557
558     c=safe_malloc(sizeof(*c),"netlink_regnets");
559     c->networks=nets;
560     c->deliver=deliver;
561     c->dst=dst;
562     c->name=client_name; /* XXX copy it? */
563     c->options=options;
564     c->link_quality=LINK_QUALITY_DOWN;
565     c->next=st->clients;
566     st->clients=c;
567     if (max_start_pad > st->max_start_pad) st->max_start_pad=max_start_pad;
568     if (max_end_pad > st->max_end_pad) st->max_end_pad=max_end_pad;
569     st->n_routes+=nets->entries;
570
571     return c;
572 }
573
574 static void netlink_dump_routes(struct netlink *st, bool_t requested)
575 {
576     int i;
577     string_t net;
578     uint32_t c=M_INFO;
579
580     if (requested) c=M_WARNING;
581     Message(c,"%s: routing table:\n",st->name);
582     for (i=0; i<st->n_routes; i++) {
583         net=subnet_to_string(&st->routes[i].net);
584         Message(c,"%s -> tunnel %s (%s,%s route,%s,quality %d)\n",net,
585                 st->routes[i].c->name,
586                 st->routes[i].hard?"hard":"soft",
587                 st->routes[i].allow_route?"free":"restricted",
588                 st->routes[i].up?"up":"down",
589                 st->routes[i].quality);
590         free(net);
591     }
592     Message(c,"%s/32 -> netlink \"%s\"\n",
593             ipaddr_to_string(st->secnet_address),st->name);
594     for (i=0; i<st->networks.entries; i++) {
595         net=subnet_to_string(&st->networks.list[i]);
596         Message(c,"%s -> host\n",net);
597         free(net);
598     }
599 }
600
601 static int netlink_compare_route_specificity(const void *ap, const void *bp)
602 {
603     const struct netlink_route *a=ap;
604     const struct netlink_route *b=bp;
605
606     if (a->net.len==b->net.len) return 0;
607     if (a->net.len<b->net.len) return 1;
608     return -1;
609 }
610
611 static void netlink_phase_hook(void *sst, uint32_t new_phase)
612 {
613     struct netlink *st=sst;
614     struct netlink_client *c;
615     uint32_t i,j;
616
617     /* All the networks serviced by the various tunnels should now
618      * have been registered.  We build a routing table by sorting the
619      * routes into most-specific-first order.  */
620     st->routes=safe_malloc(st->n_routes*sizeof(*st->routes),
621                            "netlink_phase_hook");
622     /* Fill the table */
623     i=0;
624     for (c=st->clients; c; c=c->next) {
625         for (j=0; j<c->networks->entries; j++) {
626             st->routes[i].net=c->networks->list[j];
627             st->routes[i].c=c;
628             /* Hard routes are always up;
629                soft routes default to down */
630             st->routes[i].up=c->options&NETLINK_OPTION_SOFTROUTE?False:True;
631             st->routes[i].kup=False;
632             st->routes[i].hard=c->options&NETLINK_OPTION_SOFTROUTE?False:True;
633             st->routes[i].allow_route=c->options&NETLINK_OPTION_ALLOW_ROUTE?
634                 True:False;
635             st->routes[i].quality=c->link_quality;
636             i++;
637         }
638     }
639     /* ASSERT i==st->n_routes */
640     if (i!=st->n_routes) {
641         fatal("netlink: route count error: expected %d got %d\n",
642               st->n_routes,i);
643     }
644     /* Sort the table in descending order of specificity */
645     qsort(st->routes,st->n_routes,sizeof(*st->routes),
646           netlink_compare_route_specificity);
647
648     netlink_dump_routes(st,False);
649 }
650
651 static void netlink_signal_handler(void *sst, int signum)
652 {
653     struct netlink *st=sst;
654     Message(M_INFO,"%s: route dump requested by SIGUSR1\n",st->name);
655     netlink_dump_routes(st,True);
656 }
657
658 netlink_deliver_fn *netlink_init(struct netlink *st,
659                                  void *dst, struct cloc loc,
660                                  dict_t *dict, string_t description,
661                                  netlink_route_fn *set_route,
662                                  netlink_deliver_fn *to_host)
663 {
664     st->dst=dst;
665     st->cl.description=description;
666     st->cl.type=CL_NETLINK;
667     st->cl.apply=NULL;
668     st->cl.interface=&st->ops;
669     st->ops.st=st;
670     st->ops.regnets=netlink_regnets;
671     st->ops.deliver=netlink_incoming;
672     st->ops.set_quality=netlink_set_quality;
673     st->max_start_pad=0;
674     st->max_end_pad=0;
675     st->clients=NULL;
676     st->set_route=set_route;
677     st->deliver_to_host=to_host;
678
679     st->name=dict_read_string(dict,"name",False,"netlink",loc);
680     if (!st->name) st->name=description;
681     dict_read_subnet_list(dict, "networks", True, "netlink", loc,
682                           &st->networks);
683     dict_read_subnet_list(dict, "exclude-remote-networks", False, "netlink",
684                           loc, &st->exclude_remote_networks);
685     /* secnet-address does not have to be in local-networks;
686        however, it should be advertised in the 'sites' file for the
687        local site. */
688     st->secnet_address=string_to_ipaddr(
689         dict_find_item(dict,"secnet-address", True, "netlink", loc),"netlink");
690     st->mtu=dict_read_number(dict, "mtu", False, "netlink", loc, DEFAULT_MTU);
691     buffer_new(&st->icmp,ICMP_BUFSIZE);
692     st->n_routes=0;
693     st->routes=NULL;
694
695     add_hook(PHASE_SETUP,netlink_phase_hook,st);
696     request_signal_notification(SIGUSR1, netlink_signal_handler, st);
697
698     return netlink_incoming;
699 }
700
701 /* No connection to the kernel at all... */
702
703 struct null {
704     struct netlink nl;
705 };
706
707 static bool_t null_set_route(void *sst, struct netlink_route *route)
708 {
709     struct null *st=sst;
710     string_t t;
711
712     if (route->up!=route->kup) {
713         t=subnet_to_string(&route->net);
714         Message(M_INFO,"%s: setting route %s to state %s\n",st->nl.name,
715                 t, route->up?"up":"down");
716         free(t);
717         route->kup=route->up;
718         return True;
719     }
720     return False;
721 }
722             
723 static void null_deliver(void *sst, void *cid, struct buffer_if *buf)
724 {
725     return;
726 }
727
728 static list_t *null_apply(closure_t *self, struct cloc loc, dict_t *context,
729                           list_t *args)
730 {
731     struct null *st;
732     item_t *item;
733     dict_t *dict;
734
735     st=safe_malloc(sizeof(*st),"null_apply");
736
737     item=list_elem(args,0);
738     if (!item || item->type!=t_dict)
739         cfgfatal(loc,"null-netlink","parameter must be a dictionary\n");
740     
741     dict=item->data.dict;
742
743     netlink_init(&st->nl,st,loc,dict,"null-netlink",null_set_route,
744                  null_deliver);
745
746     return new_closure(&st->nl.cl);
747 }
748
749 init_module netlink_module;
750 void netlink_module(dict_t *dict)
751 {
752     add_closure(dict,"null-netlink",null_apply);
753 }