forked from Stichting-MINIX-Research-Foundation/minix
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathroute.c
More file actions
1654 lines (1397 loc) · 51.1 KB
/
Copy pathroute.c
File metadata and controls
1654 lines (1397 loc) · 51.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* LWIP service - route.c - route management */
/*
* This module provides a destination-based routing implementation, roughly
* matching the routing as done traditionally by the BSDs and by current NetBSD
* in particular. As such, this implementation almost completely replaces
* lwIP's own more limited (and less rigid) routing algorithms. It does this
* using a combination of overriding lwIP functions (ip4_route, ip6_route) with
* weak-symbol patching, and lwIP-provided gateway hooks. Especially the
* former gives us a level of control that lwIP's routing hooks do not provide:
* not only does such overriding give us the ability to flag that no route was
* found at all, we also bypass a number of default decisions taken by lwIP
* where the routing hooks are not called at all.
*
* As a result, the routing tables as visible to the user are an almost
* completely accurate reflection of the routing decisions taken by this TCP/IP
* stack in practice. There is currently only one exception: for IPv4 gateway
* selection, lwIP will bypass the gateway hook if the given address is on the
* local subnet according to the locally assigned IP address and subnet mask.
* This exception should practically affect noone, though.
*
* Our routing implementation differs from NetBSD's in various aspects, though.
* Perhaps the most important one, also noted elsewhere, is that we do not
* support the coexistence of an all-bits-set network route and a host route
* for the same IP address. If necessary, this issue can be resolved.
*
* We use a custom concept of "immutable" routes for local addresses, which are
* a somewhat special case as explained in the ifaddr module. Since those
* RTF_LOCAL routes cannot be deleted, a small change is made to the route(8)
* flush-all command to skip them. Packets directed at local addresses on
* non-loopback interfaces are handled in a way that differs from NetBSD's,
* too. This is explained in the ifdev module.
*
* The BSDs support special routes that reject or blackhole packets, based on
* routing flags. We support such routes as well, but implement them somewhat
* differently from the BSDs: such packets always get routed over a loopback
* interface (regardless of their associated interface), in order to save on
* routing lookups for packets in the common case.
*
* As general rules of thumb: if there is no route to a destination, assignment
* of a local address will already fail with a "no route to host" error. If
* there is an RTF_REJECT route, a local address will be assigned, but actual
* packets will be routed to a loopback interface and result in a "no route to
* host" error upon reception there - this is what NetBSD seems to do too, even
* though the documentation says that RTF_REJECT routes generate ICMP messages
* instead. RTF_BLACKHOLE behaves similarly to RTF_REJECT, except that the
* packet is simply discarded upon receipt by the loopback interface.
*
* In various places, both here and elsewhere, we check to make sure that on
* routing and output, scoped IPv6 source and destination addresses never leave
* their zone. For example, a packet must not be sent to an outgoing interface
* if its source address is a link-local address with a zone for another
* interface. lwIP does not check for such violations, and so we must make
* sure that this does not happen ourselves.
*
* Normally, one would tell lwIP to use a particular default IPv4 gateway by
* associating the gateway address to a particular interface, and then setting
* that interface as default interface (netif_default). We explicitly do
* neither of these things. Instead, the routing hooks should return the
* default route whenever applicable, and the gateway hooks should return the
* default route's gateway IP address whenever needed.
*
* Due to lwIP's limited set of error codes, we do not properly distinguish
* between cases where EHOSTUNREACH or ENETUNREACH should be thrown, and throw
* the former in most cases.
*/
#include "lwip.h"
#include "ifaddr.h"
#include "rttree.h"
#include "rtsock.h"
#include "route.h"
#include "lldata.h"
#include "lwip/nd6.h"
/*
* The maximum number of uint8_t bytes needed to represent a routing address.
* This value is the maximum of 4 (for IPv4) and 16 (for IPv6).
*/
#define ROUTE_ADDR_MAX (MAX(IP4_BITS, IP6_BITS) / NBBY)
/*
* We use a shared routing entry data structure for IPv4 and IPv6 routing
* entries. The result is cleaner code at the cost of (currently) about 2.3KB
* of memory wasted (costing 12 bytes per address for three addresses for 64 of
* the 128 routing entries that would be for IPv4), although with the benefit
* that either address family may use more than half of the routing entries.
* From that 2.3KB, 1KB can be reclaimed by moving the destination address and
* mask into the rttree_entry data structure, at the cost of its generality.
*/
struct route_entry {
struct rttree_entry re_entry; /* routing tree entry */
union pxfer_re_pu {
struct ifdev *repu_ifdev; /* associated interface */
SIMPLEQ_ENTRY(route_entry) repu_next; /* next free pointer */
} re_pu;
unsigned int re_flags; /* routing flags (RTF_) */
unsigned int re_use; /* number of times used */
uint8_t re_addr[ROUTE_ADDR_MAX]; /* destination address */
uint8_t re_mask[ROUTE_ADDR_MAX]; /* destination mask */
union ixfer_re_gu {
ip4_addr_p_t regu_gw4; /* gateway (IPv4) */
ip6_addr_p_t regu_gw6; /* gateway (IPv6) */
} re_gu;
};
#define re_ifdev re_pu.repu_ifdev
#define re_next re_pu.repu_next
#define re_gw4 re_gu.regu_gw4
#define re_gw6 re_gu.regu_gw6
/* Routes for local addresses are immutable, for reasons explained in ifdev. */
#define route_is_immutable(route) ((route)->re_flags & RTF_LOCAL)
/*
* We override a subset of the BSD routing flags in order to store our own
* local settings. In particular, we have to have a way to store whether a
* route is for an IPv4 or IPv6 destination address. We override BSD's
* RTF_DONE flag for this: RTF_DONE is only used with routing sockets, and
* never associated with actual routes. In contrast, RTF_IPV6 is only used
* with actual routes, and never sent across routing sockets. In general,
* overriding flags is preferable to adding new ones, as BSD might later add
* more flags itself as well, while it can never remove existing flags.
*/
#define RTF_IPV6 RTF_DONE /* route is for an IPv6 destination */
/* The total number of routing entries (IPv4 and IPv6 combined). */
#define NR_ROUTE_ENTRY 128
static struct route_entry route_array[NR_ROUTE_ENTRY]; /* routing entries */
static SIMPLEQ_HEAD(, route_entry) route_freelist; /* free entry list */
/* The routing trees. There are two: one for IPv4 and one for IPv6. */
#define ROUTE_TREE_V4 0
#define ROUTE_TREE_V6 1
#define NR_ROUTE_TREE 2
static struct rttree route_tree[NR_ROUTE_TREE];
/* We support a single cached routing entry per address family (IPv4, IPv6). */
static int rtcache_v4set;
static ip4_addr_t rtcache_v4addr;
static struct route_entry *rtcache_v4route;
static int rtcache_v6set;
static ip6_addr_t rtcache_v6addr;
static struct route_entry *rtcache_v6route;
/*
* Initialize the routing cache. There are a lot of trivial functions here,
* but this is designed to be extended in the future.
*/
static void
rtcache_init(void)
{
rtcache_v4set = FALSE;
rtcache_v6set = FALSE;
}
/*
* Look up the given IPv4 address in the routing cache. If there is a match,
* return TRUE with the associated route in 'route', possibly NULL if a
* negative result was cached. Return FALSE if the routing cache does not
* cache the given IPv4 address.
*/
static inline int
rtcache_lookup_v4(const ip4_addr_t * ipaddr, struct route_entry ** route)
{
if (rtcache_v4set && ip4_addr_cmp(&rtcache_v4addr, ipaddr)) {
*route = rtcache_v4route;
return TRUE;
} else
return FALSE;
}
/*
* Add the given IPv4 address and the given routing entry (NULL for negative
* caching) to the routing cache.
*/
static inline void
rtcache_add_v4(const ip4_addr_t * ipaddr, struct route_entry * route)
{
rtcache_v4addr = *ipaddr;
rtcache_v4route = route;
rtcache_v4set = TRUE;
}
/*
* Reset the IPv4 routing cache.
*/
static void
rtcache_reset_v4(void)
{
rtcache_v4set = FALSE;
}
/*
* Look up the given IPv6 address in the routing cache. If there is a match,
* return TRUE with the associated route in 'route', possibly NULL if a
* negative result was cached. Return FALSE if the routing cache does not
* cache the given IPv6 address.
*/
static inline int
rtcache_lookup_v6(const ip6_addr_t * ipaddr, struct route_entry ** route)
{
if (rtcache_v6set && ip6_addr_cmp(&rtcache_v6addr, ipaddr)) {
*route = rtcache_v6route;
return TRUE;
} else
return FALSE;
}
/*
* Add the given IPv6 address and the given routing entry (NULL for negative
* caching) to the routing cache. Caching of scoped addresses without zones is
* not supported.
*/
static inline void
rtcache_add_v6(const ip6_addr_t * ipaddr, struct route_entry * route)
{
rtcache_v6addr = *ipaddr;
rtcache_v6route = route;
rtcache_v6set = TRUE;
}
/*
* Reset the IPv6 routing cache.
*/
static void
rtcache_reset_v6(void)
{
rtcache_v6set = FALSE;
}
/*
* Initialize the routing module.
*/
void
route_init(void)
{
unsigned int slot;
/* Initialize the routing trees. */
rttree_init(&route_tree[ROUTE_TREE_V4], IP4_BITS);
rttree_init(&route_tree[ROUTE_TREE_V6], IP6_BITS);
/* Initialize the list of free routing entries. */
SIMPLEQ_INIT(&route_freelist);
for (slot = 0; slot < __arraycount(route_array); slot++)
SIMPLEQ_INSERT_TAIL(&route_freelist, &route_array[slot],
re_next);
/* Reset the routing cache. */
rtcache_init();
}
/*
* Prepare for a routing tree operation by converting the given IPv4 address
* into a raw address that can be used in that routing tree operation.
*/
static inline void
route_prepare_v4(const ip4_addr_t * ip4addr, uint8_t rtaddr[ROUTE_ADDR_MAX])
{
uint32_t val;
val = ip4_addr_get_u32(ip4addr);
memcpy(rtaddr, &val, sizeof(val));
}
/*
* Prepare for a routing tree operation by converting the given IPv6 address
* into a raw address that can be used in that routing tree operation. If the
* given prefix length allows for it, also incorporate the address zone.
*/
static inline void
route_prepare_v6(const ip6_addr_t * ip6addr, unsigned int prefix,
uint8_t rtaddr[ROUTE_ADDR_MAX])
{
assert(sizeof(ip6addr->addr) == IP6_BITS / NBBY);
/*
* TODO: in most cases, we could actually return a pointer to the
* address contained in the given lwIP IP address structure. However,
* doing so would make a lot things quite a bit messier around here,
* but the small performance gain may still make it worth it.
*/
memcpy(rtaddr, ip6addr->addr, sizeof(ip6addr->addr));
/*
* Embed the zone ID into the address, KAME style. This is the
* easiest way to have link-local addresses for multiple interfaces
* coexist in a single routing tree. Do this only if the full zone ID
* would be included in the prefix though, or we might de-normalize the
* address.
*/
if (ip6_addr_has_zone(ip6addr) && prefix >= 32)
rtaddr[3] = ip6_addr_zone(ip6addr);
}
/*
* Prepare for a routing tree operation by converting the given IP address into
* a raw address that can be used in that routing tree operation. The given
* address's zone ID is embedded "KAME-style" into the raw (IPv6) address when
* applicable and if the given prefix length allows for it. Return the index
* of the routing tree to use (ROUTE_TREE_V4 or ROUTE_TREE_V6).
*/
static unsigned int
route_prepare(const ip_addr_t * ipaddr, unsigned int prefix,
uint8_t rtaddr[ROUTE_ADDR_MAX])
{
switch (IP_GET_TYPE(ipaddr)) {
case IPADDR_TYPE_V4:
route_prepare_v4(ip_2_ip4(ipaddr), rtaddr);
return ROUTE_TREE_V4;
case IPADDR_TYPE_V6:
route_prepare_v6(ip_2_ip6(ipaddr), prefix, rtaddr);
return ROUTE_TREE_V6;
default:
panic("unknown IP address type: %u", IP_GET_TYPE(ipaddr));
}
}
/*
* The given routing tree (ROUTE_TREE_V4 or ROUTE_TREE_V6) has been updated.
* Invalidate any cache entries that may now have become stale, both locally
* and in lwIP.
*/
static void
route_updated(unsigned int tree)
{
if (tree == ROUTE_TREE_V6) {
rtcache_reset_v6();
/*
* Also clear the lwIP ND6 destination cache, which may now
* contain entries for the wrong gateway.
*/
nd6_clear_destination_cache();
} else
rtcache_reset_v4();
}
/*
* Add a route to the appropriate routing table. The address, address zone,
* prefix, and RTF_HOST flag in the flags field make up the identity of the
* route. If the flags field contains RTF_GATEWAY, a gateway must be given;
* otherwise, it must be NULL. The route is associated with the given
* interface, which may not be NULL. The caller must ensure that the flags
* field does not contain unsupported flags. On success, return OK, and also
* also announce the addition. On failure, return a negative error code.
*/
int
route_add(const ip_addr_t * addr, unsigned int prefix,
const ip_addr_t * gateway, struct ifdev * ifdev, unsigned int flags,
const struct rtsock_request * rtr)
{
struct route_entry *route;
unsigned int tree, byte;
int r;
assert(flags & RTF_UP);
assert(!!(flags & RTF_GATEWAY) == (gateway != NULL));
assert(ifdev != NULL);
/* Get a routing entry, if any are available. */
if (SIMPLEQ_EMPTY(&route_freelist))
return ENOBUFS;
route = SIMPLEQ_FIRST(&route_freelist);
/*
* Perform sanity checks on the input, and fill in enough of the
* routing entry to be able to try and add it to the routing tree.
*/
memset(route->re_addr, 0, sizeof(route->re_addr));
tree = route_prepare(addr, prefix, route->re_addr);
switch (tree) {
case ROUTE_TREE_V4:
if (prefix > IP4_BITS ||
(prefix != IP4_BITS && (flags & RTF_HOST)))
return EINVAL;
flags &= ~RTF_IPV6;
break;
case ROUTE_TREE_V6:
if (prefix > IP6_BITS ||
(prefix != IP6_BITS && (flags & RTF_HOST)))
return EINVAL;
flags |= RTF_IPV6;
break;
default:
return EINVAL;
}
/* Generate the (raw) network mask. This is protocol agnostic! */
addr_make_netmask(route->re_mask, sizeof(route->re_mask), prefix);
/* The given address must be normalized to its mask. */
for (byte = 0; byte < __arraycount(route->re_addr); byte++)
if ((route->re_addr[byte] & ~route->re_mask[byte]) != 0)
return EINVAL;
/*
* Attempt to add the routing entry. Host-type entries do not have an
* associated mask, enabling ever-so-slightly faster matching.
*/
if ((r = rttree_add(&route_tree[tree], &route->re_entry,
route->re_addr, (flags & RTF_HOST) ? NULL : route->re_mask,
prefix)) != OK)
return r;
/*
* Success. Finish the routing entry. Remove the entry from the free
* list before assigning re_ifdev, as these two use the same memory.
*/
SIMPLEQ_REMOVE_HEAD(&route_freelist, re_next);
route->re_ifdev = ifdev;
route->re_flags = flags;
/*
* Store the gateway if one is given. Store the address in lwIP format
* because that is the easiest way use it later again. Store it as a
* union to keep the route entry structure as small as possible. Store
* the address without its zone, because the gateway's address zone is
* implied by its associated ifdev.
*
* If no gateway is given, this is a link-type route, i.e., a route for
* a local network, with all nodes directly connected and reachable.
*/
if (flags & RTF_GATEWAY) {
if (flags & RTF_IPV6)
ip6_addr_copy_to_packed(route->re_gw6,
*ip_2_ip6(gateway));
else
ip4_addr_copy(route->re_gw4, *ip_2_ip4(gateway));
}
/* We have made routing changes. */
route_updated(tree);
/* Announce the route addition. */
rtsock_msg_route(route, RTM_ADD, rtr);
return OK;
}
/*
* Check whether it is possible to add a route for the given destination to the
* corresponding routing table, that is, a subsequent route_add() call for this
* destination address is guaranteed to succeed (if all its parameters are
* valid). Return TRUE if adding the route is guaranteed to succeed, or FALSE
* if creating a route for the given destination would fail.
*/
int
route_can_add(const ip_addr_t * addr, unsigned int prefix,
int is_host __unused)
{
uint8_t rtaddr[ROUTE_ADDR_MAX];
unsigned int tree;
tree = route_prepare(addr, prefix, rtaddr);
/*
* The corresponding routing tree must not already contain an exact
* match for the destination. If the routing tree implementation is
* ever extended with support for coexisting host and net entries with
* the same prefix, we should also pass in 'is_host' here.
*/
if (rttree_lookup_exact(&route_tree[tree], rtaddr, prefix) != NULL)
return FALSE;
/* There must be a routing entry on the free list as well. */
return !SIMPLEQ_EMPTY(&route_freelist);
}
/*
* Find a route with the exact given route identity. Return the route if
* found, or NULL if no route exists with this identity.
*/
struct route_entry *
route_find(const ip_addr_t * addr, unsigned int prefix, int is_host)
{
struct rttree_entry *entry;
struct route_entry *route;
uint8_t rtaddr[ROUTE_ADDR_MAX];
unsigned int tree;
tree = route_prepare(addr, prefix, rtaddr);
entry = rttree_lookup_exact(&route_tree[tree], rtaddr, prefix);
if (entry == NULL)
return NULL;
route = (struct route_entry *)entry;
/*
* As long as the routing tree code does not support coexisting host
* and net entries with the same prefix, we have to check the type.
*/
if (!!(route->re_flags & RTF_HOST) != is_host)
return NULL;
return route;
}
/*
* A route lookup failed for the given IP address. Generate an RTM_MISS
* message on routing sockets.
*/
static void
route_miss(const ip_addr_t * ipaddr)
{
union sockaddr_any addr;
socklen_t addr_len;
addr_len = sizeof(addr);
addr_put_inet(&addr.sa, &addr_len, ipaddr, TRUE /*kame*/, 0 /*port*/);
rtsock_msg_miss(&addr.sa);
}
/*
* A route lookup failed for the given IPv4 address. Generate an RTM_MISS
* message on routing sockets.
*/
static void
route_miss_v4(const ip4_addr_t * ip4addr)
{
ip_addr_t ipaddr;
ip_addr_copy_from_ip4(ipaddr, *ip4addr);
route_miss(&ipaddr);
}
/*
* A route lookup failed for the given IPv6 address. Generate an RTM_MISS
* message on routing sockets.
*/
static void
route_miss_v6(const ip6_addr_t * ip6addr)
{
ip_addr_t ipaddr;
ip_addr_copy_from_ip6(ipaddr, *ip6addr);
route_miss(&ipaddr);
}
/*
* Look up the most narrow matching routing entry for the given IPv4 address.
* Return the routing entry if one exists at all, or NULL otherwise. This
* function performs caching.
*/
static inline struct route_entry *
route_lookup_v4(const ip4_addr_t * ip4addr)
{
uint8_t rtaddr[ROUTE_ADDR_MAX];
struct route_entry *route;
/*
* Look up the route for the destination IP address, unless we have a
* cached route entry. We cache negatives in order to avoid generating
* lots of RTM_MISS messages for the same destination in a row.
*/
if (rtcache_lookup_v4(ip4addr, &route))
return route;
route_prepare_v4(ip4addr, rtaddr);
route = (struct route_entry *)
rttree_lookup_match(&route_tree[ROUTE_TREE_V4], rtaddr);
/* Cache the result, even if we found no route. */
rtcache_add_v4(ip4addr, route);
return route;
}
/*
* Look up the most narrow matching routing entry for the given IPv6 address,
* taking into account its zone ID if applicable. Return the routing entry if
* one exists at all, or NULL otherwise. This function performs caching.
*/
static inline struct route_entry *
route_lookup_v6(const ip6_addr_t * ip6addr)
{
uint8_t rtaddr[ROUTE_ADDR_MAX];
struct route_entry *route;
int use_cache;
/*
* We do not support caching of addresses that should have a zone but
* do not: in different contexts, such addresses could yield different
* routes.
*/
use_cache = !ip6_addr_lacks_zone(ip6addr, IP6_UNKNOWN);
if (use_cache && rtcache_lookup_v6(ip6addr, &route))
return route;
route_prepare_v6(ip6addr, IP6_BITS, rtaddr);
route = (struct route_entry *)
rttree_lookup_match(&route_tree[ROUTE_TREE_V6], rtaddr);
/* Cache the result, even if no route was found. */
if (use_cache)
rtcache_add_v6(ip6addr, route);
return route;
}
/*
* Look up the most narrow matching routing entry for the given IP address,
* taking into account its zone ID if applicable. Return the routing entry if
* one exists at all, or NULL otherwise. This function performs caching.
*/
struct route_entry *
route_lookup(const ip_addr_t * addr)
{
if (IP_IS_V4(addr))
return route_lookup_v4(ip_2_ip4(addr));
else
return route_lookup_v6(ip_2_ip6(addr));
}
/*
* Change an existing routing entry. Its flags are always updated to the new
* set of given flags, although certain flags are always preserved. If the
* new flags set has RTF_GATEWAY set and 'gateway' is not NULL, update the
* gateway associated with the route. If 'ifdev' is not NULL, reassociate the
* route with the given interface; this will not affect the zone of the
* route's destination address. On success, return OK, and also announce the
* change. On failure, return a negative error code.
*/
static int
route_change(struct route_entry * route, const ip_addr_t * gateway,
struct ifdev * ifdev, unsigned int flags,
const struct rtsock_request * rtr)
{
unsigned int tree, preserve;
tree = (route->re_flags & RTF_IPV6) ? ROUTE_TREE_V6 : ROUTE_TREE_V4;
/* Update the associated interface (only) if a new one is given. */
if (ifdev != NULL)
route->re_ifdev = ifdev;
/*
* These flags may not be changed. RTF_UP should always be set anyway.
* RTF_HOST and RTF_IPV6 are part of the route's identity. RTF_LOCAL
* should be preserved as well, although we will not get here if either
* the old or the new flags have it set anyway.
*/
preserve = RTF_UP | RTF_HOST | RTF_IPV6 | RTF_LOCAL;
/* Always update the flags. There is no way not to. */
route->re_flags = (route->re_flags & preserve) | (flags & ~preserve);
/*
* If a new gateway is given *and* RTF_GATEWAY is set, update the
* gateway. If RTF_GATEWAY is not set, this is a link-type route with
* no gateway. If no new gateway is given, we keep the gateway as is.
*/
if (gateway != NULL && (flags & RTF_GATEWAY)) {
if (flags & RTF_IPV6)
ip6_addr_copy_to_packed(route->re_gw6,
*ip_2_ip6(gateway));
else
ip4_addr_copy(route->re_gw4, *ip_2_ip4(gateway));
}
/* We have made routing changes. */
route_updated(tree);
/* Announce the route change. */
rtsock_msg_route(route, RTM_CHANGE, rtr);
return OK;
}
/*
* Delete the given route, and announce its deletion.
*/
void
route_delete(struct route_entry * route, const struct rtsock_request * rtr)
{
unsigned int tree;
/* First announce the deletion, while the route is still around. */
tree = (route->re_flags & RTF_IPV6) ? ROUTE_TREE_V6 : ROUTE_TREE_V4;
rtsock_msg_route(route, RTM_DELETE, rtr);
/* Then actually delete the route. */
rttree_delete(&route_tree[tree], &route->re_entry);
SIMPLEQ_INSERT_HEAD(&route_freelist, route, re_next);
/* We have made routing changes. */
route_updated(tree);
}
/*
* Delete all routes associated with the given interface, typically as part of
* destroying the interface.
*/
void
route_clear(struct ifdev * ifdev)
{
struct rttree_entry *entry, *parent;
struct route_entry *route;
unsigned int tree;
/*
* Delete all routes associated with the given interface. Fortunately,
* we need not also delete addresses zoned to the given interface,
* because no route can be created with a zone ID that does not match
* the associated interface. That is the main reason why we ignore
* zone IDs for gateways when adding or changing routes..
*/
for (tree = 0; tree < NR_ROUTE_TREE; tree++) {
parent = NULL;
while ((entry = rttree_enum(&route_tree[tree],
parent)) != NULL) {
route = (struct route_entry *)entry;
if (route->re_ifdev == ifdev)
route_delete(route, NULL /*request*/);
else
parent = entry;
}
}
}
/*
* Process a routing command specifically for an IPv4 or IPv6 route, as one of
* the specific continuations of processing started by route_process(). The
* RTM_ routing command is given as 'type'. The route destination is given as
* 'dst_addr'; its address type determines whether the operation is for IPv4 or
* IPv6. The sockaddr structures for 'mask' and 'gateway' are passed on as is
* and may have to be parsed here if not NULL. 'ifdev' is the interface to be
* associated with a route; it is non-NULL only if an interface name (IFP) or
* address (IFA) was given. The RTF_ flags field 'flags' has been checked
* against the globally supported flags, but may have to be checked for flags
* that do not apply to IPv4/IPv6 routes. Return OK or a negative error code,
* following the same semantics as route_process().
*/
static int
route_process_inet(unsigned int type, const ip_addr_t * dst_addr,
const struct sockaddr * mask, const struct sockaddr * gateway,
struct ifdev * ifdev, unsigned int flags,
const struct rtsock_request * rtr)
{
struct route_entry *route;
ip_addr_t gw_storage, *gw_addr;
struct ifdev *ifdev2;
uint32_t zone;
unsigned int prefix;
int r;
assert(!(flags & RTF_LLDATA));
if ((flags & (RTF_DYNAMIC | RTF_MODIFIED | RTF_DONE | RTF_XRESOLVE |
RTF_LLINFO | RTF_CLONED | RTF_SRC | RTF_ANNOUNCE |
RTF_BROADCAST)) != 0)
return EINVAL;
/*
* For network entries, a network mask must be provided in all cases.
* For host entries, the network mask is ignored, and we use a prefix
* with all bits set.
*/
if (!(flags & RTF_HOST)) {
if (mask == NULL)
return EINVAL;
if ((r = addr_get_netmask(mask, mask->sa_len,
IP_GET_TYPE(dst_addr), &prefix, NULL /*ipaddr*/)) != OK)
return r;
} else {
if (IP_IS_V4(dst_addr))
prefix = IP4_BITS;
else
prefix = IP6_BITS;
}
gw_addr = NULL;
/*
* Determine the gateway and interface for the routing entry, if
* applicable.
*/
if (type == RTM_ADD || type == RTM_CHANGE) {
/*
* The RTF_UP flag must always be set, but only if the flags
* field is used at all.
*/
if (!(flags & RTF_UP))
return EINVAL;
if ((flags & RTF_GATEWAY) && gateway != NULL) {
if ((r = addr_get_inet(gateway, gateway->sa_len,
IP_GET_TYPE(dst_addr), &gw_storage, TRUE /*kame*/,
NULL /*port*/)) != OK)
return r;
gw_addr = &gw_storage;
/*
* We use the zone of the gateway to help determine the
* interface, but we do not reject a mismatching zone
* here. The reason for this is that we do not want
* routes that have zones for an interface other than
* the one associated with the route, as that could
* create a world of trouble: packets leaving their
* zone, complications with cleaning up interfaces..
*/
if (IP_IS_V6(gw_addr) &&
ip6_addr_has_zone(ip_2_ip6(gw_addr))) {
zone = ip6_addr_zone(ip_2_ip6(gw_addr));
ifdev2 = ifdev_get_by_index(zone);
if (ifdev != NULL && ifdev != ifdev2)
return EINVAL;
else
ifdev = ifdev2;
}
/*
* If we still have no interface at this point, see if
* we can find one based on just the gateway address.
* See if a locally attached network owns the address.
* That may not succeed, leaving ifdev set to NULL.
*/
if (ifdev == NULL)
ifdev = ifaddr_map_by_subnet(gw_addr);
}
/*
* When adding routes, all necessary information must be given.
* When changing routes, we can leave some settings as is.
*/
if (type == RTM_ADD) {
if ((flags & RTF_GATEWAY) && gw_addr == NULL)
return EINVAL;
/* TODO: try harder to find a matching interface.. */
if (ifdev == NULL)
return ENETUNREACH;
}
}
/*
* All route commands except RTM_ADD require that a route exists for
* the given identity, although RTM_GET, when requesting a host entry,
* may return a wider (network) route based on just the destination
* address.
*/
if (type != RTM_ADD) {
/* For RTM_GET (only), a host query may return a net route. */
if (type == RTM_GET && (flags & RTF_HOST))
route = route_lookup(dst_addr);
else
route = route_find(dst_addr, prefix,
!!(flags & RTF_HOST));
if (route == NULL)
return ESRCH;
} else
route = NULL;
/* Process the actual routing command. */
switch (type) {
case RTM_ADD:
return route_add(dst_addr, prefix, gw_addr, ifdev, flags, rtr);
case RTM_CHANGE:
/* Routes for local addresses are immutable. */
if (route_is_immutable(route))
return EPERM;
return route_change(route, gw_addr, ifdev, flags, rtr);
case RTM_DELETE:
/* Routes for local addresses are immutable. */
if (route_is_immutable(route))
return EPERM;
route_delete(route, rtr);
return OK;
case RTM_LOCK:
/*
* TODO: implement even the suggestion that we support this.
* For now, we do not keep per-route metrics, let alone change
* them dynamically ourselves, so "locking" metrics is really
* not a concept that applies to us. We may however have to
* save the lock mask and return it in queries..
*/
/* FALLTHROUGH */
case RTM_GET:
/* Simply generate a message for the route we just found. */
rtsock_msg_route(route, type, rtr);
return OK;
default:
return EINVAL;
}
}
/*
* Process a routing command from a routing socket. The RTM_ type of command
* is given as 'type', and is one of RTM_ADD, RTM_CHANGE, RTM_DELETE, RTM_GET,
* RTM_LOCK. In addition, the function takes a set of sockaddr pointers as
* provided by the routing command. Each of these sockaddr pointers may be
* NULL; if not NULL, the structure is at least large enough to contain the
* address length (sa_len) and family (sa_family), and the length never exceeds
* the amount of memory used to store the sockaddr structure. However, the
* length itself has not yet been checked against the expected protocol
* structure and could even be zero. The command's RTF_ routing flags and
* metrics are provided as well. On success, return OK, in which case the
* caller assumes that a routing socket announcement for the processed command
* has been sent already (passing on 'rtr' to the announcement function as is).
* On failure, return a negative error code; in that case, the caller will send
* a failure response on the original routing socket itself.
*/
int
route_process(unsigned int type, const struct sockaddr * dst,
const struct sockaddr * mask, const struct sockaddr * gateway,
const struct sockaddr * ifp, const struct sockaddr * ifa,
unsigned int flags, unsigned long inits,
const struct rt_metrics * rmx, const struct rtsock_request * rtr)
{
struct ifdev *ifdev, *ifdev2;
char name[IFNAMSIZ];
ip_addr_t dst_addr, if_addr;
uint32_t zone;
uint8_t addr_type;
int r;
/*
* The identity of a route is determined by its destination address,
* destination zone, prefix length, and whether it is a host entry
* or not. If it is a host entry (RTF_HOST is set), the prefix length
* is implied by the protocol; otherwise it should be obtained from the
* given netmask if necessary. For link-local addresses, the zone ID
* must be embedded KAME-style in the destination address. A
* destination address must always be given. The destination address
* also determines the overall address family.
*/
if (dst == NULL)
return EINVAL;
switch (dst->sa_family) {
case AF_INET:
addr_type = IPADDR_TYPE_V4;
break;
#ifdef INET6
case AF_INET6:
addr_type = IPADDR_TYPE_V6;
break;
#endif /* INET6 */
default:
return EAFNOSUPPORT;
}