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