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