Import release 0.1.5
[secnet] / netlink.c
CommitLineData
2fe58dfd
SE
1/* User-kernel network link */
2
baa06aeb
SE
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. */
2fe58dfd
SE
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
8689b3a9 11#include "secnet.h"
2fe58dfd 12#include "util.h"
9d3a4132 13#include "netlink.h"
2fe58dfd 14
4efd681a
SE
15/* Generic IP checksum routine */
16static inline uint16_t ip_csum(uint8_t *iph,uint32_t count)
2fe58dfd 17{
4efd681a
SE
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);
2fe58dfd
SE
30}
31
4efd681a
SE
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 */
40static 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
501: 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
602:
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
70static inline uint16_t ip_fast_csum(uint8_t *iph, uint32_t ihl)
2fe58dfd 71{
4efd681a
SE
72 return ip_csum(iph,ihl*4);
73}
74#endif
75
76struct 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
96struct 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
70dc107b
SE
116static void netlink_packet_deliver(struct netlink *st,
117 struct netlink_client *client,
118 struct buffer_if *buf);
4efd681a
SE
119
120static 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 */
148static 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 */
175static 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. */
196static 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
70dc107b
SE
208/* client indicates where the packet we're constructing a response to
209 comes from. NULL indicates the host. */
4efd681a 210static void netlink_icmp_simple(struct netlink *st, struct buffer_if *buf,
70dc107b 211 struct netlink_client *client,
4efd681a
SE
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);
70dc107b 224 netlink_packet_deliver(st,NULL,&st->icmp);
4efd681a
SE
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 */
240static 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
9d3a4132
SE
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;
4efd681a
SE
248 len=ntohs(iph->tot_len);
249 /* There should be no padding */
9d3a4132 250 if (buf->size!=len || len<(iph->ihl<<2)) return False;
4efd681a
SE
251 /* XXX check that there's no source route specified */
252 return True;
253}
254
70dc107b
SE
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.) */
258static void netlink_packet_deliver(struct netlink *st,
259 struct netlink_client *client,
260 struct buffer_if *buf)
4efd681a
SE
261{
262 struct iphdr *iph=(struct iphdr *)buf->start;
263 uint32_t dest=ntohl(iph->daddr);
70dc107b
SE
264 uint32_t source=ntohl(iph->saddr);
265 uint32_t best_quality;
266 int best_match;
267 int i;
2fe58dfd 268
4efd681a 269 BUF_ASSERT_USED(buf);
2fe58dfd 270
4efd681a
SE
271 if (dest==st->secnet_address) {
272 Message(M_ERROR,"%s: trying to deliver a packet to myself!\n");
273 BUF_FREE(buf);
2fe58dfd
SE
274 return;
275 }
4efd681a 276
3454dce4
SE
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. */
70dc107b
SE
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++) {
9d3a4132 288 if (st->routes[i].up && subnet_match(&st->routes[i].net,dest)) {
70dc107b
SE
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");
9d3a4132
SE
311 /* XXX I think this could also occur if a soft tunnel just
312 went down, but still had packets queued in the kernel. */
70dc107b
SE
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);
4efd681a
SE
323 BUF_ASSERT_FREE(buf);
324 } else {
325 /* Generate ICMP destination unreachable */
70dc107b 326 netlink_icmp_simple(st,buf,client,3,0); /* client==NULL */
4efd681a
SE
327 BUF_FREE(buf);
328 }
70dc107b
SE
329 }
330 } else { /* client is set */
331 /* We know the origin is a tunnel - packet must be for the host */
3454dce4
SE
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 */
70dc107b
SE
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);
2fe58dfd
SE
343 }
344 }
70dc107b 345 BUF_ASSERT_FREE(buf);
4efd681a
SE
346}
347
70dc107b
SE
348static void netlink_packet_forward(struct netlink *st,
349 struct netlink_client *client,
350 struct buffer_if *buf)
4efd681a
SE
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 */
70dc107b 359 netlink_icmp_simple(st,buf,client,11,0);
4efd681a
SE
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
70dc107b 367 netlink_packet_deliver(st,client,buf);
4efd681a
SE
368 BUF_ASSERT_FREE(buf);
369}
370
9d3a4132 371/* Deal with packets addressed explicitly to us */
70dc107b
SE
372static void netlink_packet_local(struct netlink *st,
373 struct netlink_client *client,
374 struct buffer_if *buf)
4efd681a
SE
375{
376 struct icmphdr *h;
377
378 h=(struct icmphdr *)buf->start;
379
380 if ((ntohs(h->iph.frag_off)&0xbfff)!=0) {
9d3a4132
SE
381 Message(M_WARNING,"%s: fragmented packet addressed to secnet; "
382 "ignoring it\n",st->name);
4efd681a
SE
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);
70dc107b 399 netlink_packet_deliver(st,NULL,buf);
4efd681a
SE
400 return;
401 }
402 Message(M_WARNING,"%s: unknown incoming ICMP\n",st->name);
403 } else {
404 /* Send ICMP protocol unreachable */
70dc107b 405 netlink_icmp_simple(st,buf,client,3,2);
4efd681a
SE
406 BUF_FREE(buf);
407 return;
408 }
409
410 BUF_FREE(buf);
411}
412
9d3a4132
SE
413/* If cid==NULL packet is from host, otherwise cid specifies which tunnel
414 it came from. */
415static void netlink_incoming(void *sst, void *cid, struct buffer_if *buf)
4efd681a
SE
416{
417 struct netlink *st=sst;
9d3a4132 418 struct netlink_client *client=cid;
4efd681a
SE
419 uint32_t source,dest;
420 struct iphdr *iph;
421
422 BUF_ASSERT_USED(buf);
423 if (!netlink_check(st,buf)) {
9d3a4132
SE
424 Message(M_WARNING,"%s: bad IP packet from %s\n",
425 st->name,client?client->name:"host");
4efd681a
SE
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
9d3a4132
SE
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 }
4efd681a 459 }
9d3a4132
SE
460 /* (st->secnet_address needs checking before matching destination
461 addresses) */
2fe58dfd 462 if (dest==st->secnet_address) {
9d3a4132 463 netlink_packet_local(st,client,buf);
4efd681a 464 BUF_ASSERT_FREE(buf);
2fe58dfd
SE
465 return;
466 }
9d3a4132 467 if (client) {
3454dce4 468 /* Check for free routing */
9d3a4132
SE
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 }
2fe58dfd 480 }
70dc107b 481 netlink_packet_forward(st,client,buf);
4efd681a
SE
482 BUF_ASSERT_FREE(buf);
483}
484
9d3a4132
SE
485static void netlink_set_softlinks(struct netlink *st, struct netlink_client *c,
486 bool_t up)
4efd681a 487{
9d3a4132 488 uint32_t i;
4efd681a 489
9d3a4132
SE
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 }
4efd681a 496 }
4efd681a
SE
497}
498
70dc107b 499static void netlink_set_quality(void *sst, void *cid, uint32_t quality)
4efd681a 500{
9d3a4132 501 struct netlink *st=sst;
4efd681a
SE
502 struct netlink_client *c=cid;
503
70dc107b 504 c->link_quality=quality;
9d3a4132
SE
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 }
4efd681a
SE
510}
511
512static 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,
3454dce4 515 uint32_t options, string_t client_name)
4efd681a
SE
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
3454dce4 524 if ((options&NETLINK_OPTION_SOFTROUTE) && !st->set_route) {
9d3a4132
SE
525 Message(M_ERROR,"%s: this netlink device does not support "
526 "soft routes.\n");
527 return NULL;
528 }
529
3454dce4 530 if (options&NETLINK_OPTION_SOFTROUTE) {
9d3a4132 531 /* XXX for now we assume that soft routes require root privilege;
3454dce4 532 this may not always be true. The device driver can tell us. */
9d3a4132
SE
533 require_root_privileges=True;
534 require_root_privileges_explanation="netlink: soft routes";
535 }
536
9d3a4132
SE
537 /* Check that nets do not intersect st->exclude_remote_networks;
538 refuse to register if they do. */
baa06aeb
SE
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
4efd681a
SE
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? */
3454dce4 551 c->options=options;
70dc107b 552 c->link_quality=LINK_QUALITY_DOWN;
4efd681a
SE
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;
70dc107b 557 st->n_routes+=nets->entries;
4efd681a
SE
558
559 return c;
560}
561
9d3a4132
SE
562static 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);
3454dce4
SE
570 Message(M_INFO,"%s -> tunnel %s (%s,%s route,%s)\n",net,
571 st->routes[i].c->name,
9d3a4132 572 st->routes[i].hard?"hard":"soft",
3454dce4 573 st->routes[i].allow_route?"free":"restricted",
9d3a4132
SE
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
70dc107b
SE
586static 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
596static 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;
3454dce4
SE
613 /* Hard routes are always up;
614 soft routes default to down */
615 st->routes[i].up=c->options&NETLINK_OPTION_SOFTROUTE?False:True;
9d3a4132 616 st->routes[i].kup=False;
3454dce4
SE
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;
70dc107b
SE
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);
9d3a4132
SE
631
632 netlink_dump_routes(st);
70dc107b
SE
633}
634
9d3a4132
SE
635netlink_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)
4efd681a
SE
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;
9d3a4132 648 st->ops.deliver=netlink_incoming;
70dc107b 649 st->ops.set_quality=netlink_set_quality;
4efd681a
SE
650 st->max_start_pad=0;
651 st->max_end_pad=0;
652 st->clients=NULL;
9d3a4132 653 st->set_route=set_route;
4efd681a
SE
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);
baa06aeb
SE
660 dict_read_subnet_list(dict, "exclude-remote-networks", False, "netlink",
661 loc, &st->exclude_remote_networks);
b2a56f7c
SE
662 /* secnet-address does not have to be in local-networks;
663 however, it should be advertised in the 'sites' file for the
baa06aeb 664 local site. */
4efd681a
SE
665 st->secnet_address=string_to_ipaddr(
666 dict_find_item(dict,"secnet-address", True, "netlink", loc),"netlink");
4efd681a
SE
667 st->mtu=dict_read_number(dict, "mtu", False, "netlink", loc, DEFAULT_MTU);
668 buffer_new(&st->icmp,ICMP_BUFSIZE);
70dc107b
SE
669 st->n_routes=0;
670 st->routes=NULL;
671
672 add_hook(PHASE_SETUP,netlink_phase_hook,st);
4efd681a 673
9d3a4132 674 return netlink_incoming;
2fe58dfd
SE
675}
676
9d3a4132 677/* No connection to the kernel at all... */
2fe58dfd 678
9d3a4132 679struct null {
4efd681a 680 struct netlink nl;
4efd681a 681};
2fe58dfd 682
9d3a4132 683static bool_t null_set_route(void *sst, struct netlink_route *route)
4efd681a 684{
9d3a4132
SE
685 struct null *st=sst;
686 string_t t;
4efd681a 687
9d3a4132
SE
688 if (route->up!=route->kup) {
689 t=subnet_to_string(&route->net);
690 Message(M_INFO,"%s: setting route %s to state %s\n",st->nl.name,
691 t, route->up?"up":"down");
692 free(t);
693 route->kup=route->up;
694 return True;
2fe58dfd 695 }
9d3a4132 696 return False;
2fe58dfd 697}
9d3a4132 698
2fe58dfd
SE
699static void null_deliver(void *sst, void *cid, struct buffer_if *buf)
700{
701 return;
702}
703
704static list_t *null_apply(closure_t *self, struct cloc loc, dict_t *context,
705 list_t *args)
706{
707 struct null *st;
4efd681a
SE
708 item_t *item;
709 dict_t *dict;
2fe58dfd 710
4efd681a 711 st=safe_malloc(sizeof(*st),"null_apply");
2fe58dfd 712
4efd681a
SE
713 item=list_elem(args,0);
714 if (!item || item->type!=t_dict)
715 cfgfatal(loc,"null-netlink","parameter must be a dictionary\n");
716
717 dict=item->data.dict;
718
9d3a4132
SE
719 netlink_init(&st->nl,st,loc,dict,"null-netlink",null_set_route,
720 null_deliver);
4efd681a
SE
721
722 return new_closure(&st->nl.cl);
2fe58dfd
SE
723}
724
725init_module netlink_module;
726void netlink_module(dict_t *dict)
727{
4efd681a 728 add_closure(dict,"null-netlink",null_apply);
2fe58dfd 729}