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