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

Reply via email to