diff options
author | Susant Sahani <ssahani@vmware.com> | 2019-09-14 16:44:22 +0530 |
---|---|---|
committer | Susant Sahani <ssahani@vmware.com> | 2019-09-17 12:09:59 +0200 |
commit | 203d4df5732b1fdcf50db498ddeb74a934b21f87 (patch) | |
tree | 1408614e0f70c9d3d170f7f4389d25511694b332 /src/libsystemd-network/sd-radv.c | |
parent | 61cda4d7964ff00dfa6260f84219720d9b97671a (diff) | |
download | systemd-203d4df5732b1fdcf50db498ddeb74a934b21f87.tar.gz systemd-203d4df5732b1fdcf50db498ddeb74a934b21f87.tar.bz2 systemd-203d4df5732b1fdcf50db498ddeb74a934b21f87.zip |
network: Add support to advertie ipv6 route
Implements https://tools.ietf.org/html/rfc4191
cat veth99.network
```
[Match]
Name=veth99
[Network]
DHCP=no
IPv6PrefixDelegation=yes
Address=2001:db8:0:1::1/64
[IPv6Prefix]
Prefix=2001:db8:0:1::4/64
[IPv6RoutePrefix]
Route=2001:db0:fff::/48
```
Wireshark
```
Frame 481: 142 bytes on wire (1136 bits), 142 bytes captured (1136 bits) on interface 0
Ethernet II, Src: 1e:04:f8:b8:2f:d4 (1e:04:f8:b8:2f:d4), Dst: IPv6mcast_01 (33:33:00:00:00:01)
Internet Protocol Version 6, Src: fe80::1c04:f8ff:feb8:2fd4, Dst: ff02::1
Internet Control Message Protocol v6
Type: Router Advertisement (134)
Code: 0
Checksum: 0xec77 [correct]
[Checksum Status: Good]
Cur hop limit: 0
Flags: 0x00, Prf (Default Router Preference): Medium
Router lifetime (s): 0
Reachable time (ms): 0
Retrans timer (ms): 0
ICMPv6 Option (Source link-layer address : 1e:04:f8:b8:2f:d4)
Type: Source link-layer address (1)
Length: 1 (8 bytes)
Link-layer address: 1e:04:f8:b8:2f:d4 (1e:04:f8:b8:2f:d4)
ICMPv6 Option (MTU : 1500)
Type: MTU (5)
Length: 1 (8 bytes)
Reserved
MTU: 1500
ICMPv6 Option (Prefix information : 2001:db8:0:1::4/64)
Type: Prefix information (3)
Length: 4 (32 bytes)
Prefix Length: 64
Flag: 0xc0, On-link flag(L), Autonomous address-configuration flag(A)
Valid Lifetime: 2592000
Preferred Lifetime: 604800
Reserved
Prefix: 2001:db8:0:1::4
ICMPv6 Option (Route Information : Medium 2001:db0:fff::/48)
Type: Route Information (24)
Length: 3 (24 bytes)
Prefix Length: 48
Flag: 0x00, Route Preference: Medium
...0 0... = Route Preference: Medium (0)
000. .000 = Reserved: 0
Route Lifetime: 604800
Prefix: 2001:db0:fff::
```
Diffstat (limited to 'src/libsystemd-network/sd-radv.c')
-rw-r--r-- | src/libsystemd-network/sd-radv.c | 130 |
1 files changed, 128 insertions, 2 deletions
diff --git a/src/libsystemd-network/sd-radv.c b/src/libsystemd-network/sd-radv.c index 185b55e1c5..d531f52326 100644 --- a/src/libsystemd-network/sd-radv.c +++ b/src/libsystemd-network/sd-radv.c @@ -116,6 +116,7 @@ static sd_radv *radv_free(sd_radv *ra) { DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv, sd_radv, radv_free); static int radv_send(sd_radv *ra, const struct in6_addr *dst, uint32_t router_lifetime) { + sd_radv_route_prefix *rt; sd_radv_prefix *p; struct sockaddr_in6 dst_addr = { .sin6_family = AF_INET6, @@ -136,9 +137,9 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, uint32_t router_li .nd_opt_mtu_type = ND_OPT_MTU, .nd_opt_mtu_len = 1, }; - /* Reserve iov space for RA header, linkaddr, MTU, N prefixes, RDNSS + /* Reserve iov space for RA header, linkaddr, MTU, N prefixes, N routes, RDNSS and DNSSL */ - struct iovec iov[5 + ra->n_prefixes]; + struct iovec iov[5 + ra->n_prefixes + ra->n_route_prefixes]; struct msghdr msg = { .msg_name = &dst_addr, .msg_namelen = sizeof(dst_addr), @@ -190,6 +191,9 @@ static int radv_send(sd_radv *ra, const struct in6_addr *dst, uint32_t router_li iov[msg.msg_iovlen++] = IOVEC_MAKE(&p->opt, sizeof(p->opt)); } + LIST_FOREACH(prefix, rt, ra->route_prefixes) + iov[msg.msg_iovlen++] = IOVEC_MAKE(&rt->opt, sizeof(rt->opt)); + if (ra->rdnss) iov[msg.msg_iovlen++] = IOVEC_MAKE(ra->rdnss, ra->rdnss->length * 8); @@ -606,6 +610,77 @@ _public_ sd_radv_prefix *sd_radv_remove_prefix(sd_radv *ra, return cur; } +_public_ int sd_radv_add_route_prefix(sd_radv *ra, sd_radv_route_prefix *p, int dynamic) { + char time_string_valid[FORMAT_TIMESPAN_MAX]; + usec_t time_now, valid, valid_until; + _cleanup_free_ char *pretty = NULL; + sd_radv_route_prefix *cur; + int r; + + assert_return(ra, -EINVAL); + + if (!p) + return -EINVAL; + + (void) in_addr_to_string(AF_INET6, + (union in_addr_union*) &p->opt.in6_addr, + &pretty); + + LIST_FOREACH(prefix, cur, ra->route_prefixes) { + _cleanup_free_ char *addr = NULL; + + r = in_addr_prefix_intersect(AF_INET6, + (union in_addr_union*) &cur->opt.in6_addr, + cur->opt.prefixlen, + (union in_addr_union*) &p->opt.in6_addr, + p->opt.prefixlen); + if (r < 0) + return r; + if (r == 0) + continue; + + if (dynamic && cur->opt.prefixlen == p->opt.prefixlen) + goto update; + + (void) in_addr_to_string(AF_INET6, + (union in_addr_union*) &cur->opt.in6_addr, + &addr); + log_radv("IPv6 route prefix %s/%u already configured, ignoring %s/%u", + strempty(addr), cur->opt.prefixlen, + strempty(pretty), p->opt.prefixlen); + + return -EEXIST; + } + + p = sd_radv_route_prefix_ref(p); + + LIST_APPEND(prefix, ra->route_prefixes, p); + ra->n_route_prefixes++; + + cur = p; + if (!dynamic) { + log_radv("Added prefix %s/%u", strempty(pretty), p->opt.prefixlen); + return 0; + } + + update: + r = sd_event_now(ra->event, clock_boottime_or_monotonic(), &time_now); + if (r < 0) + return r; + + valid = be32toh(p->opt.lifetime) * USEC_PER_SEC; + valid_until = usec_add(valid, time_now); + if (valid_until == USEC_INFINITY) + return -EOVERFLOW; + + log_radv("%s route prefix %s/%u valid %s", + cur? "Updated": "Added", + strempty(pretty), p->opt.prefixlen, + format_timespan(time_string_valid, FORMAT_TIMESPAN_MAX, valid, USEC_PER_SEC)); + + return 0; +} + _public_ int sd_radv_set_rdnss(sd_radv *ra, uint32_t lifetime, const struct in6_addr *dns, size_t n_dns) { _cleanup_free_ struct sd_radv_opt_dns *opt_rdnss = NULL; @@ -770,3 +845,54 @@ _public_ int sd_radv_prefix_set_preferred_lifetime(sd_radv_prefix *p, return 0; } + +_public_ int sd_radv_route_prefix_new(sd_radv_route_prefix **ret) { + sd_radv_route_prefix *p; + + assert_return(ret, -EINVAL); + + p = new(sd_radv_route_prefix, 1); + if (!p) + return -ENOMEM; + + *p = (sd_radv_route_prefix) { + .n_ref = 1, + + .opt.type = SD_RADV_OPT_ROUTE_INFORMATION, + .opt.length = DIV_ROUND_UP(sizeof(p->opt), 8), + .opt.prefixlen = 64, + + .opt.lifetime = htobe32(604800), + }; + + *ret = p; + return 0; +} + +DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_radv_route_prefix, sd_radv_route_prefix, mfree); + +_public_ int sd_radv_prefix_set_route_prefix(sd_radv_route_prefix *p, const struct in6_addr *in6_addr, + unsigned char prefixlen) { + assert_return(p, -EINVAL); + assert_return(in6_addr, -EINVAL); + + if (prefixlen > 128) + return -EINVAL; + + if (prefixlen > 64) + /* unusual but allowed, log it */ + log_radv("Unusual prefix length %u greater than 64", prefixlen); + + p->opt.in6_addr = *in6_addr; + p->opt.prefixlen = prefixlen; + + return 0; +} + +_public_ int sd_radv_route_prefix_set_lifetime(sd_radv_route_prefix *p, uint32_t valid_lifetime) { + assert_return(p, -EINVAL); + + p->opt.lifetime = htobe32(valid_lifetime); + + return 0; +} |