packets: Change IPv6 functions to more closely resemble IPv4 ones.
[cascardo/ovs.git] / lib / packets.c
index a4d7854..866d782 100644 (file)
@@ -404,51 +404,72 @@ ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *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. */
-void
-format_ipv6_addr(char *addr_str, const struct in6_addr *addr)
+/* 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)
 {
-    inet_ntop(AF_INET6, addr, addr_str, INET6_ADDRSTRLEN);
+    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 (ovs_scan(s, IP_SCAN_FMT"%n", IP_SCAN_ARGS(ip), &n) && !s[n]) {
+        *mask = OVS_BE32_MAX;
+    } else {
+        return xasprintf("%s: invalid IP address", s);
+    }
+    return NULL;
 }
 
 void
-print_ipv6_addr(struct ds *string, const struct in6_addr *addr)
+ipv6_format_addr(const struct in6_addr *addr, struct ds *s)
 {
     char *dst;
 
-    ds_reserve(string, string->length + INET6_ADDRSTRLEN);
+    ds_reserve(s, s->length + INET6_ADDRSTRLEN);
 
-    dst = string->string + string->length;
-    format_ipv6_addr(dst, addr);
-    string->length += strlen(dst);
+    dst = s->string + s->length;
+    inet_ntop(AF_INET6, addr, dst, INET6_ADDRSTRLEN);
+    s->length += strlen(dst);
 }
 
 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);
         }
     }
 }
@@ -875,6 +896,24 @@ packet_set_sctp_port(struct dp_packet *packet, ovs_be16 src, ovs_be16 dst)
     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) {
@@ -1062,3 +1101,4 @@ packet_csum_pseudoheader(const struct ip_header *ip)
 
     return partial;
 }
+