const struct in6_addr in6addr_exact = IN6ADDR_EXACT_INIT;
const struct in6_addr in6addr_all_hosts = IN6ADDR_ALL_HOSTS_INIT;
+struct in6_addr
+flow_tnl_dst(const struct flow_tnl *tnl)
+{
+ return tnl->ip_dst ? in6_addr_mapped_ipv4(tnl->ip_dst) : tnl->ipv6_dst;
+}
+
+struct in6_addr
+flow_tnl_src(const struct flow_tnl *tnl)
+{
+ return tnl->ip_src ? in6_addr_mapped_ipv4(tnl->ip_src) : tnl->ipv6_src;
+}
+
/* Parses 's' as a 16-digit hexadecimal number representing a datapath ID. On
* success stores the dpid into '*dpidp' and returns true, on failure stores 0
* into '*dpidp' and returns false.
}
}
+/* Parses string 's', which must be an IP address. Stores the IP address into
+ * '*ip'. Returns true if successful, otherwise false. */
+bool
+ip_parse(const char *s, ovs_be32 *ip)
+{
+ return inet_pton(AF_INET, s, ip) == 1;
+}
+
+/* Parses string 's', which must be an IP address with an optional netmask or
+ * CIDR prefix length. Stores the IP address into '*ip' and the netmask into
+ * '*mask'. (If 's' does not contain a netmask, 255.255.255.255 is
+ * assumed.)
+ *
+ * Returns NULL if successful, otherwise an error message that the caller must
+ * free(). */
+char * OVS_WARN_UNUSED_RESULT
+ip_parse_masked(const char *s, ovs_be32 *ip, ovs_be32 *mask)
+{
+ int prefix;
+ int n;
+
+ if (ovs_scan(s, IP_SCAN_FMT"/"IP_SCAN_FMT"%n",
+ IP_SCAN_ARGS(ip), IP_SCAN_ARGS(mask), &n) && !s[n]) {
+ /* OK. */
+ } else if (ovs_scan(s, IP_SCAN_FMT"/%d%n", IP_SCAN_ARGS(ip), &prefix, &n)
+ && !s[n]) {
+ if (prefix <= 0 || prefix > 32) {
+ return xasprintf("%s: network prefix bits not between 0 and "
+ "32", s);
+ }
+ *mask = be32_prefix_mask(prefix);
+ } else if (ip_parse(s, ip)) {
+ *mask = OVS_BE32_MAX;
+ } else {
+ return xasprintf("%s: invalid IP address", s);
+ }
+ return NULL;
+}
+
+/* Similar to ip_parse_masked(), but the mask, if present, must be a CIDR mask
+ * and is returned as a prefix length in '*plen'. */
+char * OVS_WARN_UNUSED_RESULT
+ip_parse_cidr(const char *s, ovs_be32 *ip, unsigned int *plen)
+{
+ ovs_be32 mask;
+ char *error;
+
+ error = ip_parse_masked(s, ip, &mask);
+ if (error) {
+ return error;
+ }
+
+ if (!ip_is_cidr(mask)) {
+ return xasprintf("%s: CIDR network required", s);
+ }
+ *plen = ip_count_cidr_bits(mask);
+ return NULL;
+}
+
+/* Parses string 's', which must be an IPv6 address. Stores the IPv6 address
+ * into '*ip'. Returns true if successful, otherwise false. */
+bool
+ipv6_parse(const char *s, struct in6_addr *ip)
+{
+ return inet_pton(AF_INET6, s, ip) == 1;
+}
+
+/* Parses string 's', which must be an IPv6 address with an optional netmask or
+ * CIDR prefix length. Stores the IPv6 address into '*ip' and the netmask into
+ * '*mask'. (If 's' does not contain a netmask, all-one-bits is assumed.)
+ *
+ * Returns NULL if successful, otherwise an error message that the caller must
+ * free(). */
+char * OVS_WARN_UNUSED_RESULT
+ipv6_parse_masked(const char *s, struct in6_addr *ip, struct in6_addr *mask)
+{
+ char ipv6_s[IPV6_SCAN_LEN + 1];
+ int prefix;
+ int n;
+
+ if (ovs_scan(s, IPV6_SCAN_FMT"%n", ipv6_s, &n) && ipv6_parse(ipv6_s, ip)) {
+ s += n;
+ if (!*s) {
+ *mask = in6addr_exact;
+ } else if (ovs_scan(s, "/%d%n", &prefix, &n) && !s[n]) {
+ if (prefix <= 0 || prefix > 128) {
+ return xasprintf("%s: IPv6 network prefix bits not between 0 "
+ "and 128", s);
+ }
+ *mask = ipv6_create_mask(prefix);
+ } else if (ovs_scan(s, "/"IPV6_SCAN_FMT"%n", ipv6_s, &n)
+ && !s[n]
+ && ipv6_parse(ipv6_s, mask)) {
+ /* OK. */
+ } else {
+ return xasprintf("%s: syntax error expecting IPv6 prefix length "
+ "or mask", s);
+ }
+ return NULL;
+ }
+ return xasprintf("%s: invalid IPv6 address", s);
+}
+
+/* Similar to ipv6_parse_masked(), but the mask, if present, must be a CIDR
+ * mask and is returned as a prefix length in '*plen'. */
+char * OVS_WARN_UNUSED_RESULT
+ipv6_parse_cidr(const char *s, struct in6_addr *ip, unsigned int *plen)
+{
+ struct in6_addr mask;
+ char *error;
+
+ error = ipv6_parse_masked(s, ip, &mask);
+ if (error) {
+ return error;
+ }
+
+ if (!ipv6_is_cidr(&mask)) {
+ return xasprintf("%s: IPv6 CIDR network required", s);
+ }
+ *plen = ipv6_count_cidr_bits(&mask);
+ return NULL;
+}
/* Stores the string representation of the IPv6 address 'addr' into the
* character array 'addr_str', which must be at least INET6_ADDRSTRLEN
* bytes long. */
void
-format_ipv6_addr(char *addr_str, const struct in6_addr *addr)
+ipv6_format_addr(const struct in6_addr *addr, struct ds *s)
{
- inet_ntop(AF_INET6, addr, addr_str, INET6_ADDRSTRLEN);
+ char *dst;
+
+ ds_reserve(s, s->length + INET6_ADDRSTRLEN);
+
+ dst = s->string + s->length;
+ inet_ntop(AF_INET6, addr, dst, INET6_ADDRSTRLEN);
+ s->length += strlen(dst);
}
+/* Same as print_ipv6_addr, but optionally encloses the address in square
+ * brackets. */
void
-print_ipv6_addr(struct ds *string, const struct in6_addr *addr)
+ipv6_format_addr_bracket(const struct in6_addr *addr, struct ds *s,
+ bool bracket)
{
- char *dst;
-
- ds_reserve(string, string->length + INET6_ADDRSTRLEN);
-
- dst = string->string + string->length;
- format_ipv6_addr(dst, addr);
- string->length += strlen(dst);
+ if (bracket) {
+ ds_put_char(s, '[');
+ }
+ ipv6_format_addr(addr, s);
+ if (bracket) {
+ ds_put_char(s, ']');
+ }
}
void
-print_ipv6_mapped(struct ds *s, const struct in6_addr *addr)
+ipv6_format_mapped(const struct in6_addr *addr, struct ds *s)
{
if (IN6_IS_ADDR_V4MAPPED(addr)) {
ds_put_format(s, IP_FMT, addr->s6_addr[12], addr->s6_addr[13],
addr->s6_addr[14], addr->s6_addr[15]);
} else {
- print_ipv6_addr(s, addr);
+ ipv6_format_addr(addr, s);
}
}
void
-print_ipv6_masked(struct ds *s, const struct in6_addr *addr,
- const struct in6_addr *mask)
+ipv6_format_masked(const struct in6_addr *addr, const struct in6_addr *mask,
+ struct ds *s)
{
- print_ipv6_addr(s, addr);
+ ipv6_format_addr(addr, s);
if (mask && !ipv6_mask_is_exact(mask)) {
if (ipv6_is_cidr(mask)) {
int cidr_bits = ipv6_count_cidr_bits(mask);
ds_put_format(s, "/%d", cidr_bits);
} else {
ds_put_char(s, '/');
- print_ipv6_addr(s, mask);
+ ipv6_format_addr(mask, s);
}
}
}
+/* Stores the string representation of the IPv6 address 'addr' into the
+ * character array 'addr_str', which must be at least INET6_ADDRSTRLEN
+ * bytes long. If addr is IPv4-mapped, store an IPv4 dotted-decimal string. */
+const char *
+ipv6_string_mapped(char *addr_str, const struct in6_addr *addr)
+{
+ ovs_be32 ip;
+ ip = in6_addr_get_mapped_ipv4(addr);
+ if (ip) {
+ return inet_ntop(AF_INET, &ip, addr_str, INET6_ADDRSTRLEN);
+ } else {
+ return inet_ntop(AF_INET6, addr, addr_str, INET6_ADDRSTRLEN);
+ }
+}
+
struct in6_addr ipv6_addr_bitand(const struct in6_addr *a,
const struct in6_addr *b)
{
put_16aligned_be32(&sh->sctp_csum, old_csum ^ old_correct_csum ^ new_csum);
}
+/* Sets the ICMP type and code of the ICMP header contained in 'packet'.
+ * 'packet' must be a valid ICMP packet with its l4 offset properly
+ * populated. */
+void
+packet_set_icmp(struct dp_packet *packet, uint8_t type, uint8_t code)
+{
+ struct icmp_header *ih = dp_packet_l4(packet);
+ ovs_be16 orig_tc = htons(ih->icmp_type << 8 | ih->icmp_code);
+ ovs_be16 new_tc = htons(type << 8 | code);
+
+ if (orig_tc != new_tc) {
+ ih->icmp_type = type;
+ ih->icmp_code = code;
+
+ ih->icmp_csum = recalc_csum16(ih->icmp_csum, orig_tc, new_tc);
+ }
+}
+
void
packet_set_nd(struct dp_packet *packet, const ovs_be32 target[4],
const struct eth_addr sll, const struct eth_addr tll) {
dp_packet_set_l3(b, arp);
}
+void
+compose_nd(struct dp_packet *b, const struct eth_addr eth_src,
+ struct in6_addr * ipv6_src, struct in6_addr * ipv6_dst)
+{
+ struct in6_addr sn_addr;
+ struct eth_addr eth_dst;
+ struct ovs_nd_msg *ns;
+ struct ovs_nd_opt *nd_opt;
+
+ in6_addr_solicited_node(&sn_addr, ipv6_dst);
+ ipv6_multicast_to_ethernet(ð_dst, &sn_addr);
+
+ eth_compose(b, eth_dst, eth_src, ETH_TYPE_IPV6,
+ IPV6_HEADER_LEN + ICMP6_HEADER_LEN + ND_OPT_LEN);
+ packet_set_ipv6(b, IPPROTO_ICMPV6,
+ ALIGNED_CAST(ovs_be32 *, ipv6_src->s6_addr),
+ ALIGNED_CAST(ovs_be32 *, sn_addr.s6_addr),
+ 0, 0, 255);
+
+ ns = dp_packet_l4(b);
+ nd_opt = &ns->options[0];
+
+ ns->icmph.icmp6_type = ND_NEIGHBOR_SOLICIT;
+ ns->icmph.icmp6_code = 0;
+
+ nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
+ packet_set_nd(b, ALIGNED_CAST(ovs_be32 *, ipv6_dst->s6_addr),
+ eth_src, eth_addr_zero);
+}
+
uint32_t
packet_csum_pseudoheader(const struct ip_header *ip)
{
return partial;
}
+
+#ifndef __CHECKER__
+uint32_t
+packet_csum_pseudoheader6(const struct ovs_16aligned_ip6_hdr *ip6)
+{
+ uint32_t partial = 0;
+
+ partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[0])));
+ partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[1])));
+ partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[2])));
+ partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_src.be32[3])));
+ partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[0])));
+ partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[1])));
+ partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[2])));
+ partial = csum_add32(partial, get_16aligned_be32(&(ip6->ip6_dst.be32[3])));
+
+ partial = csum_add16(partial, 0);
+ partial = csum_add16(partial, ip6->ip6_plen);
+ partial = csum_add16(partial, 0);
+ partial = csum_add16(partial, ip6->ip6_nxt);
+
+ return partial;
+}
+#endif