This patch introduces methods to compose a Router Advertisement (RA) packet, introduces flags for RA. RA packet composed structures against specification in RFC4861.
Caller can use compse_nd_ra to compose a basic RA packet, and use - packet_put_ra_sll_opt to append a Source Link-layer Address Option to RA packet, - packet_put_ra_mtu_opt to append a MTU Option to RA packet, - packet_put_ra_prefix_opt to append a Prefix Information Option to a RA packet. v1 -> v2 rebased, separate ovs_nd_opt rename in following patch. --- lib/packets.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/packets.h | 57 +++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) diff --git a/lib/packets.c b/lib/packets.c index e4c29d5..0bfd8e0 100644 --- a/lib/packets.c +++ b/lib/packets.c @@ -35,6 +35,7 @@ const struct in6_addr in6addr_exact = IN6ADDR_EXACT_INIT; const struct in6_addr in6addr_all_hosts = IN6ADDR_ALL_HOSTS_INIT; +const struct in6_addr in6addr_all_routers = IN6ADDR_ALL_ROUTERS_INIT; struct in6_addr flow_tnl_dst(const struct flow_tnl *tnl) @@ -1419,6 +1420,121 @@ compose_nd_na(struct dp_packet *b, ND_MSG_LEN + ND_OPT_LEN)); } +/* Compose an IPv6 Neighbor Discovery Router Advertisement message without + * any IPv6 Neighbor Discovery options. */ +void +compose_nd_ra(struct dp_packet *b, + const struct eth_addr eth_src, const struct eth_addr eth_dst, + const struct in6_addr *ipv6_src, const struct in6_addr *ipv6_dst, + uint8_t cur_hop_limit, uint8_t mo_flags, ovs_be16 router_lifetime, + ovs_be32 reachable_time, ovs_be32 retrans_timer) +{ + struct ovs_ra_msg *ra; + uint32_t icmp_csum; + + eth_compose(b, eth_dst, eth_src, ETH_TYPE_IPV6, IPV6_HEADER_LEN); + ra = compose_ipv6(b, IPPROTO_ICMPV6, ipv6_src, ipv6_dst, 0, 0, 255, + RA_MSG_LEN); + + ra->icmph.icmp6_type = ND_ROUTER_ADVERT; + ra->icmph.icmp6_code = 0; + ra->cur_hop_limit = cur_hop_limit; + ra->mo_flags = mo_flags; + ra->router_lifetime = router_lifetime; + ra->reachable_time = reachable_time; + ra->retrans_timer = retrans_timer; + + ra->icmph.icmp6_cksum = 0; + icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); + ra->icmph.icmp6_cksum = csum_finish(csum_continue( + icmp_csum, ra, RA_MSG_LEN)); +} + +/* Append an IPv6 Neighbor Discovery Prefix Information option to a + * Router Advertisement message. */ +void +packet_put_ra_prefix_opt(struct dp_packet *b, + uint8_t plen, uint8_t la_flags, ovs_be32 valid_lifetime, + ovs_be32 preferred_lifetime, const ovs_be32 prefix[4]) +{ + size_t prev_l4_size = dp_packet_l4_size(b); + struct ip6_hdr *nh = dp_packet_l3(b); + nh->ip6_plen = htons(prev_l4_size + ND_PREFIX_OPT_LEN); + + struct ovs_ra_msg *ra = dp_packet_l4(b); + struct ovs_nd_prefix_opt *prefix_opt; + uint32_t icmp_csum; + + prefix_opt = dp_packet_put_uninit(b, sizeof(struct ovs_nd_prefix_opt)); + prefix_opt->type = ND_OPT_PREFIX_INFORMATION; + prefix_opt->len = 4; + prefix_opt->prefix_len = plen; + prefix_opt->la_flags = la_flags; + prefix_opt->valid_lifetime = valid_lifetime; + prefix_opt->preferred_lifetime = preferred_lifetime; + prefix_opt->reserved = 0; + packet_update_csum128(b, IPPROTO_ICMPV6, prefix_opt->prefix.be32, prefix); + memcpy(prefix_opt->prefix.be32, prefix, sizeof(ovs_be32[4])); + + ra->icmph.icmp6_cksum = 0; + icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); + ra->icmph.icmp6_cksum = csum_finish(csum_continue( + icmp_csum, ra, prev_l4_size + ND_PREFIX_OPT_LEN)); +} + +/* Append an IPv6 Neighbor Discovery MTU option to a + * Router Advertisement message. */ +void +packet_put_ra_mtu_opt(struct dp_packet *b, ovs_be32 mtu) +{ + size_t prev_l4_size = dp_packet_l4_size(b); + struct ip6_hdr *nh = dp_packet_l3(b); + nh->ip6_plen = htons(prev_l4_size + ND_MTU_OPT_LEN); + + struct ovs_ra_msg *ra = dp_packet_l4(b); + struct ovs_nd_mtu_opt *mtu_opt; + uint32_t icmp_csum; + + mtu_opt = dp_packet_put_uninit(b, sizeof(struct ovs_nd_mtu_opt)); + mtu_opt->type = ND_OPT_MTU; + mtu_opt->len = 1; + mtu_opt->reserved = 0; + ovs_be16 *csum = &(ra->icmph.icmp6_cksum); + *csum = recalc_csum32(*csum, mtu_opt->mtu, mtu); + mtu_opt->mtu = mtu; + + ra->icmph.icmp6_cksum = 0; + icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); + ra->icmph.icmp6_cksum = csum_finish(csum_continue( + icmp_csum, ra, prev_l4_size + ND_MTU_OPT_LEN)); +} + +/* Append an IPv6 Neighbor Discovery Source Link-layer Address option to a + * Router Advertisement message. */ +void +packet_put_ra_sll_opt(struct dp_packet *b, const struct eth_addr lla) +{ + size_t prev_l4_size = dp_packet_l4_size(b); + struct ip6_hdr *nh = dp_packet_l3(b); + nh->ip6_plen = htons(prev_l4_size + ND_OPT_LEN); + + struct ovs_ra_msg *ra = dp_packet_l4(b); + struct ovs_nd_opt *lla_opt; + uint32_t icmp_csum; + + lla_opt = dp_packet_put_uninit(b, sizeof(struct ovs_nd_opt)); + lla_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; + lla_opt->nd_opt_len = 1; + ovs_be16 *csum = &(ra->icmph.icmp6_cksum); + *csum = recalc_csum48(*csum, lla_opt->nd_opt_mac, lla); + lla_opt->nd_opt_mac = lla; + + ra->icmph.icmp6_cksum = 0; + icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b)); + ra->icmph.icmp6_cksum = csum_finish(csum_continue( + icmp_csum, ra, prev_l4_size + ND_OPT_LEN)); +} + uint32_t packet_csum_pseudoheader(const struct ip_header *ip) { diff --git a/lib/packets.h b/lib/packets.h index dcfcd04..d0e1195 100644 --- a/lib/packets.h +++ b/lib/packets.h @@ -841,6 +841,31 @@ struct ovs_nd_opt { }; BUILD_ASSERT_DECL(ND_OPT_LEN == sizeof(struct ovs_nd_opt)); +/* Neighbor Discovery option: Prefix Information. */ +#define ND_PREFIX_OPT_LEN 32 +struct ovs_nd_prefix_opt { + uint8_t type; /* Values defined in icmp6.h */ + uint8_t len; + uint8_t prefix_len; + /* on-link flag, autonomous address-configuration flag, 6-bit reserved. */ + uint8_t la_flags; + ovs_be32 valid_lifetime; + ovs_be32 preferred_lifetime; + ovs_be32 reserved; + union ovs_16aligned_in6_addr prefix; +}; +BUILD_ASSERT_DECL(ND_PREFIX_OPT_LEN == sizeof(struct ovs_nd_prefix_opt)); + +/* Neighbor Discovery option: MTU. */ +#define ND_MTU_OPT_LEN 8 +struct ovs_nd_mtu_opt { + uint8_t type; /* Values defined in icmp6.h */ + uint8_t len; + ovs_be16 reserved; + ovs_be32 mtu; +}; +BUILD_ASSERT_DECL(ND_MTU_OPT_LEN == sizeof(struct ovs_nd_mtu_opt)); + /* Like struct nd_msg (from ndisc.h), but whereas that struct requires 32-bit * alignment, this one only requires 16-bit alignment. */ #define ND_MSG_LEN 24 @@ -852,9 +877,25 @@ struct ovs_nd_msg { }; BUILD_ASSERT_DECL(ND_MSG_LEN == sizeof(struct ovs_nd_msg)); +/* Neighbor Discovery packet flags. */ #define ND_RSO_ROUTER 0x80000000 #define ND_RSO_SOLICITED 0x40000000 #define ND_RSO_OVERRIDE 0x20000000 +#define ND_PREFIX_ON_LINK 0x80 +#define ND_PREFIX_AUTONOMOUS_ADDRESS 0x40 +#define ND_ROUTER_ADV_MANAGED_ADDRESS 0x80 +#define ND_ROUTER_ADV_OTHER_CONFIG 0x40 + +#define RA_MSG_LEN 16 +struct ovs_ra_msg { + struct icmp6_header icmph; + uint8_t cur_hop_limit; + uint8_t mo_flags; + ovs_be16 router_lifetime; + ovs_be32 reachable_time; + ovs_be32 retrans_timer; +}; +BUILD_ASSERT_DECL(RA_MSG_LEN == sizeof(struct ovs_ra_msg)); /* * Use the same struct for MLD and MLD2, naming members as the defined fields in @@ -910,6 +951,10 @@ extern const struct in6_addr in6addr_all_hosts; #define IN6ADDR_ALL_HOSTS_INIT { { { 0xff,0x02,0x00,0x00,0x00,0x00,0x00,0x00, \ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 } } } +extern const struct in6_addr in6addr_all_routers; +#define IN6ADDR_ALL_ROUTERS_INIT { { { 0xff,0x02,0x00,0x00,0x00,0x00,0x00,0x00, \ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02 } } } + static inline bool ipv6_addr_equals(const struct in6_addr *a, const struct in6_addr *b) { @@ -1108,6 +1153,18 @@ void compose_nd_na(struct dp_packet *, const struct eth_addr eth_src, const struct in6_addr *ipv6_src, const struct in6_addr *ipv6_dst, ovs_be32 rso_flags); +void compose_nd_ra(struct dp_packet *, + const struct eth_addr eth_src, const struct eth_addr eth_dst, + const struct in6_addr *ipv6_src, const struct in6_addr *ipv6_dst, + uint8_t cur_hop_limit, uint8_t mo_flags, + ovs_be16 router_lifetime, ovs_be32 reachable_time, + ovs_be32 retrans_timer); +void packet_put_ra_sll_opt(struct dp_packet *, const struct eth_addr lla); +void packet_put_ra_mtu_opt(struct dp_packet *, ovs_be32 mtu); +void packet_put_ra_prefix_opt(struct dp_packet *, + uint8_t plen, uint8_t la_flags, + ovs_be32 valid_lifetime, ovs_be32 preferred_lifetime, + const ovs_be32 router_prefix[4]); uint32_t packet_csum_pseudoheader(const struct ip_header *); void IP_ECN_set_ce(struct dp_packet *pkt, bool is_ipv6); -- 2.7.4 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev