/*
- * Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#include <config.h>
#include "packets.h"
-#include <assert.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
+#include <netinet/ip6.h>
#include <stdlib.h>
#include "byte-order.h"
#include "csum.h"
#include "flow.h"
+#include "hmap.h"
#include "dynamic-string.h"
#include "ofpbuf.h"
return *dpidp != 0;
}
-/* Returns true if 'ea' is a reserved multicast address, that a bridge must
- * never forward, false otherwise. Includes some proprietary vendor protocols
- * that shouldn't be forwarded as well.
+/* Returns true if 'ea' is a reserved address, that a bridge must never
+ * forward, false otherwise.
*
* If you change this function's behavior, please update corresponding
* documentation in vswitch.xml at the same time. */
bool
eth_addr_is_reserved(const uint8_t ea[ETH_ADDR_LEN])
{
- struct masked_eth_addr {
- uint8_t ea[ETH_ADDR_LEN];
- uint8_t mask[ETH_ADDR_LEN];
+ struct eth_addr_node {
+ struct hmap_node hmap_node;
+ uint64_t ea64;
};
- static struct masked_eth_addr mea[] = {
- { /* STP, IEEE pause frames, and other reserved protocols. */
- {0x01, 0x08, 0xc2, 0x00, 0x00, 0x00},
- {0xff, 0xff, 0xff, 0xff, 0xff, 0xf0}},
-
- { /* VRRP IPv4. */
- {0x00, 0x00, 0x5e, 0x00, 0x01, 0x00},
- {0xff, 0xff, 0xff, 0xff, 0xff, 0x00}},
-
- { /* VRRP IPv6. */
- {0x00, 0x00, 0x5e, 0x00, 0x02, 0x00},
- {0xff, 0xff, 0xff, 0xff, 0xff, 0x00}},
-
- { /* HSRPv1. */
- {0x00, 0x00, 0x0c, 0x07, 0xac, 0x00},
- {0xff, 0xff, 0xff, 0xff, 0xff, 0x00}},
-
- { /* HSRPv2. */
- {0x00, 0x00, 0x0c, 0x9f, 0xf0, 0x00},
- {0xff, 0xff, 0xff, 0xff, 0xf0, 0x00}},
-
- { /* GLBP. */
- {0x00, 0x07, 0xb4, 0x00, 0x00, 0x00},
- {0xff, 0xff, 0xff, 0x00, 0x00, 0x00}},
-
- { /* Extreme Discovery Protocol. */
- {0x00, 0xE0, 0x2B, 0x00, 0x00, 0x00},
- {0xff, 0xff, 0xff, 0xff, 0xf0, 0x00}},
-
- { /* Cisco Inter Switch Link. */
- {0x01, 0x00, 0x0c, 0x00, 0x00, 0x00},
- {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
+ static struct eth_addr_node nodes[] = {
+ /* STP, IEEE pause frames, and other reserved protocols. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000000ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000001ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000002ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000003ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000004ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000005ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000006ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000007ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000008ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c2000009ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000aULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000bULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000cULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000dULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000eULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x0108c200000fULL },
+
+ /* Extreme protocols. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x00e02b000000ULL }, /* EDP. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x00e02b000004ULL }, /* EAPS. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x00e02b000006ULL }, /* EAPS. */
+
+ /* Cisco protocols. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000c000000ULL }, /* ISL. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccccULL }, /* PAgP, UDLD, CDP,
+ * DTP, VTP. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000ccccccdULL }, /* PVST+. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000ccdcdcdULL }, /* STP Uplink Fast,
+ * FlexLink. */
+
+ /* Cisco CFM. */
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc0ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc1ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc2ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc3ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc4ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc5ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc6ULL },
+ { HMAP_NODE_NULL_INITIALIZER, 0x01000cccccc7ULL },
+ };
- { /* Cisco protocols plus others following the same pattern:
- *
- * CDP, VTP, DTP, PAgP (01-00-0c-cc-cc-cc)
- * Spanning Tree PVSTP+ (01-00-0c-cc-cc-cd)
- * STP Uplink Fast (01-00-0c-cd-cd-cd) */
- {0x01, 0x00, 0x0c, 0xcc, 0xcc, 0xcc},
- {0xff, 0xff, 0xff, 0xfe, 0xfe, 0xfe}}};
+ static struct hmap addrs = HMAP_INITIALIZER(&addrs);
+ struct eth_addr_node *node;
+ uint64_t ea64;
- size_t i;
+ if (hmap_is_empty(&addrs)) {
+ for (node = nodes; node < &nodes[ARRAY_SIZE(nodes)]; node++) {
+ hmap_insert(&addrs, &node->hmap_node,
+ hash_2words(node->ea64, node->ea64 >> 32));
+ }
+ }
- for (i = 0; i < ARRAY_SIZE(mea); i++) {
- if (eth_addr_equal_except(ea, mea[i].ea, mea[i].mask)) {
+ ea64 = eth_addr_to_uint64(ea);
+ HMAP_FOR_EACH_IN_BUCKET (node, hmap_node, hash_2words(ea64, ea64 >> 32),
+ &addrs) {
+ if (node->ea64 == ea64) {
return true;
}
}
compose_rarp(struct ofpbuf *b, const uint8_t eth_src[ETH_ADDR_LEN])
{
struct eth_header *eth;
- struct rarp_header *rarp;
+ struct arp_eth_header *arp;
ofpbuf_clear(b);
ofpbuf_prealloc_tailroom(b, ETH_HEADER_LEN + VLAN_HEADER_LEN
- + RARP_HEADER_LEN);
+ + ARP_ETH_HEADER_LEN);
ofpbuf_reserve(b, VLAN_HEADER_LEN);
eth = ofpbuf_put_uninit(b, sizeof *eth);
memcpy(eth->eth_dst, eth_addr_broadcast, ETH_ADDR_LEN);
memcpy(eth->eth_src, eth_src, ETH_ADDR_LEN);
eth->eth_type = htons(ETH_TYPE_RARP);
- rarp = ofpbuf_put_uninit(b, sizeof *rarp);
- rarp->hw_addr_space = htons(ARP_HTYPE_ETH);
- rarp->proto_addr_space = htons(ETH_TYPE_IP);
- rarp->hw_addr_length = ETH_ADDR_LEN;
- rarp->proto_addr_length = sizeof rarp->src_proto_addr;
- rarp->opcode = htons(RARP_REQUEST_REVERSE);
- memcpy(rarp->src_hw_addr, eth_src, ETH_ADDR_LEN);
- rarp->src_proto_addr = htonl(0);
- memcpy(rarp->target_hw_addr, eth_src, ETH_ADDR_LEN);
- rarp->target_proto_addr = htonl(0);
+ arp = ofpbuf_put_uninit(b, sizeof *arp);
+ arp->ar_hrd = htons(ARP_HRD_ETHERNET);
+ arp->ar_pro = htons(ARP_PRO_IP);
+ arp->ar_hln = sizeof arp->ar_sha;
+ arp->ar_pln = sizeof arp->ar_spa;
+ arp->ar_op = htons(ARP_OP_RARP);
+ memcpy(arp->ar_sha, eth_src, ETH_ADDR_LEN);
+ arp->ar_spa = htonl(0);
+ memcpy(arp->ar_tha, eth_src, ETH_ADDR_LEN);
+ arp->ar_tpa = htonl(0);
}
/* Insert VLAN header according to given TCI. Packet passed must be Ethernet
}
/* Given the IP netmask 'netmask', returns the number of bits of the IP address
- * that it specifies, that is, the number of 1-bits in 'netmask'. 'netmask'
- * must be a CIDR netmask (see ip_is_cidr()). */
+ * that it specifies, that is, the number of 1-bits in 'netmask'.
+ *
+ * If 'netmask' is not a CIDR netmask (see ip_is_cidr()), the return value will
+ * still be in the valid range but isn't otherwise meaningful. */
int
ip_count_cidr_bits(ovs_be32 netmask)
{
- assert(ip_is_cidr(netmask));
return 32 - ctz(ntohl(netmask));
}
void
ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *s)
{
- ds_put_format(s, IP_FMT, IP_ARGS(&ip));
+ ds_put_format(s, IP_FMT, IP_ARGS(ip));
if (mask != htonl(UINT32_MAX)) {
if (ip_is_cidr(mask)) {
ds_put_format(s, "/%d", ip_count_cidr_bits(mask));
} else {
- ds_put_format(s, "/"IP_FMT, IP_ARGS(&mask));
+ ds_put_format(s, "/"IP_FMT, IP_ARGS(mask));
}
}
}
/* Given the IPv6 netmask 'netmask', returns the number of bits of the IPv6
* address that it specifies, that is, the number of 1-bits in 'netmask'.
- * 'netmask' must be a CIDR netmask (see ipv6_is_cidr()). */
+ * 'netmask' must be a CIDR netmask (see ipv6_is_cidr()).
+ *
+ * If 'netmask' is not a CIDR netmask (see ipv6_is_cidr()), the return value
+ * will still be in the valid range but isn't otherwise meaningful. */
int
ipv6_count_cidr_bits(const struct in6_addr *netmask)
{
int count = 0;
const uint8_t *netmaskp = &netmask->s6_addr[0];
- assert(ipv6_is_cidr(netmask));
-
for (i=0; i<16; i++) {
if (netmaskp[i] == 0xff) {
count += 8;
*addr = new_addr;
}
+/* Returns true, if packet contains at least one routing header where
+ * segements_left > 0.
+ *
+ * This function assumes that L3 and L4 markers are set in the packet. */
+static bool
+packet_rh_present(struct ofpbuf *packet)
+{
+ const struct ip6_hdr *nh;
+ int nexthdr;
+ size_t len;
+ size_t remaining;
+ uint8_t *data = packet->l3;
+
+ remaining = (uint8_t *)packet->l4 - (uint8_t *)packet->l3;
+
+ if (remaining < sizeof *nh) {
+ return false;
+ }
+ nh = (struct ip6_hdr *)data;
+ data += sizeof *nh;
+ remaining -= sizeof *nh;
+ nexthdr = nh->ip6_nxt;
+
+ while (1) {
+ if ((nexthdr != IPPROTO_HOPOPTS)
+ && (nexthdr != IPPROTO_ROUTING)
+ && (nexthdr != IPPROTO_DSTOPTS)
+ && (nexthdr != IPPROTO_AH)
+ && (nexthdr != IPPROTO_FRAGMENT)) {
+ /* It's either a terminal header (e.g., TCP, UDP) or one we
+ * don't understand. In either case, we're done with the
+ * packet, so use it to fill in 'nw_proto'. */
+ break;
+ }
+
+ /* We only verify that at least 8 bytes of the next header are
+ * available, but many of these headers are longer. Ensure that
+ * accesses within the extension header are within those first 8
+ * bytes. All extension headers are required to be at least 8
+ * bytes. */
+ if (remaining < 8) {
+ return false;
+ }
+
+ if (nexthdr == IPPROTO_AH) {
+ /* A standard AH definition isn't available, but the fields
+ * we care about are in the same location as the generic
+ * option header--only the header length is calculated
+ * differently. */
+ const struct ip6_ext *ext_hdr = (struct ip6_ext *)data;
+
+ nexthdr = ext_hdr->ip6e_nxt;
+ len = (ext_hdr->ip6e_len + 2) * 4;
+ } else if (nexthdr == IPPROTO_FRAGMENT) {
+ const struct ip6_frag *frag_hdr = (struct ip6_frag *)data;
+
+ nexthdr = frag_hdr->ip6f_nxt;
+ len = sizeof *frag_hdr;
+ } else if (nexthdr == IPPROTO_ROUTING) {
+ const struct ip6_rthdr *rh = (struct ip6_rthdr *)data;
+
+ if (rh->ip6r_segleft > 0) {
+ return true;
+ }
+
+ nexthdr = rh->ip6r_nxt;
+ len = (rh->ip6r_len + 1) * 8;
+ } else {
+ const struct ip6_ext *ext_hdr = (struct ip6_ext *)data;
+
+ nexthdr = ext_hdr->ip6e_nxt;
+ len = (ext_hdr->ip6e_len + 1) * 8;
+ }
+
+ if (remaining < len) {
+ return false;
+ }
+ remaining -= len;
+ data += len;
+ }
+
+ return false;
+}
+
+static void
+packet_update_csum128(struct ofpbuf *packet, uint8_t proto,
+ ovs_be32 addr[4], const ovs_be32 new_addr[4])
+{
+ if (proto == IPPROTO_TCP && packet->l7) {
+ struct tcp_header *th = packet->l4;
+
+ th->tcp_csum = recalc_csum128(th->tcp_csum, addr, new_addr);
+ } else if (proto == IPPROTO_UDP && packet->l7) {
+ struct udp_header *uh = packet->l4;
+
+ if (uh->udp_csum) {
+ uh->udp_csum = recalc_csum128(uh->udp_csum, addr, new_addr);
+ if (!uh->udp_csum) {
+ uh->udp_csum = htons(0xffff);
+ }
+ }
+ }
+}
+
+static void
+packet_set_ipv6_addr(struct ofpbuf *packet, uint8_t proto,
+ struct in6_addr *addr, const ovs_be32 new_addr[4],
+ bool recalculate_csum)
+{
+ if (recalculate_csum) {
+ packet_update_csum128(packet, proto, (ovs_be32 *)addr, new_addr);
+ }
+ memcpy(addr, new_addr, sizeof(*addr));
+}
+
+static void
+packet_set_ipv6_flow_label(ovs_be32 *flow_label, ovs_be32 flow_key)
+{
+ *flow_label = (*flow_label & htonl(~IPV6_LABEL_MASK)) | flow_key;
+}
+
+static void
+packet_set_ipv6_tc(ovs_be32 *flow_label, uint8_t tc)
+{
+ *flow_label = (*flow_label & htonl(0xF00FFFFF)) | htonl(tc << 20);
+}
+
/* Modifies the IPv4 header fields of 'packet' to be consistent with 'src',
* 'dst', 'tos', and 'ttl'. Updates 'packet''s L4 checksums as appropriate.
* 'packet' must contain a valid IPv4 packet with correctly populated l[347]
}
}
+/* Modifies the IPv6 header fields of 'packet' to be consistent with 'src',
+ * 'dst', 'traffic class', and 'next hop'. Updates 'packet''s L4 checksums as
+ * appropriate. 'packet' must contain a valid IPv6 packet with correctly
+ * populated l[347] markers. */
+void
+packet_set_ipv6(struct ofpbuf *packet, uint8_t proto, const ovs_be32 src[4],
+ const ovs_be32 dst[4], uint8_t key_tc, ovs_be32 key_fl,
+ uint8_t key_hl)
+{
+ struct ip6_hdr *nh = packet->l3;
+
+ if (memcmp(&nh->ip6_src, src, sizeof(ovs_be32[4]))) {
+ packet_set_ipv6_addr(packet, proto, &nh->ip6_src, src, true);
+ }
+
+ if (memcmp(&nh->ip6_dst, dst, sizeof(ovs_be32[4]))) {
+ packet_set_ipv6_addr(packet, proto, &nh->ip6_dst, dst,
+ !packet_rh_present(packet));
+ }
+
+ packet_set_ipv6_tc(&nh->ip6_flow, key_tc);
+
+ packet_set_ipv6_flow_label(&nh->ip6_flow, key_fl);
+
+ nh->ip6_hlim = key_hl;
+}
+
static void
packet_set_port(ovs_be16 *port, ovs_be16 new_port, ovs_be16 *csum)
{
uint8_t
packet_get_tcp_flags(const struct ofpbuf *packet, const struct flow *flow)
{
- if ((flow->dl_type == htons(ETH_TYPE_IP) ||
- flow->dl_type == htons(ETH_TYPE_IPV6)) &&
- flow->nw_proto == IPPROTO_TCP && packet->l7) {
+ if (is_ip_any(flow) && flow->nw_proto == IPPROTO_TCP && packet->l7) {
const struct tcp_header *tcp = packet->l4;
return TCP_FLAGS(tcp->tcp_ctl);
} else {