Import release 0.1.7
[secnet] / 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 }