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