#include "csum.h"
#include "crc32c.h"
#include "flow.h"
-#include "hmap.h"
+#include "openvswitch/hmap.h"
#include "openvswitch/dynamic-string.h"
#include "ovs-thread.h"
#include "odp-util.h"
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)
return false;
}
+/* Attempts to parse 's' as an Ethernet address. If successful, stores the
+ * address in 'ea' and returns true, otherwise zeros 'ea' and returns
+ * false. This function checks trailing characters. */
bool
eth_addr_from_string(const char *s, struct eth_addr *ea)
{
- if (ovs_scan(s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(*ea))) {
+ int n = 0;
+ if (ovs_scan(s, ETH_ADDR_SCAN_FMT"%n", ETH_ADDR_SCAN_ARGS(*ea), &n)
+ && !s[n]) {
return true;
} else {
*ea = eth_addr_zero;
dp_packet_reset_offsets(b);
dp_packet_set_l3(b, arp);
+ b->packet_type = htonl(PT_ETH);
}
/* Insert VLAN header according to given TCI. Packet passed must be Ethernet
void
eth_pop_vlan(struct dp_packet *packet)
{
- struct vlan_eth_header *veh = dp_packet_l2(packet);
+ struct vlan_eth_header *veh = dp_packet_eth(packet);
if (veh && dp_packet_size(packet) >= sizeof *veh
&& eth_type_vlan(veh->veth_type)) {
}
}
+/* Push Ethernet header onto 'packet' assuming it is layer 3 */
+void
+push_eth(struct dp_packet *packet, const struct eth_addr *dst,
+ const struct eth_addr *src)
+{
+ struct eth_header *eh;
+
+ ovs_assert(packet->packet_type != htonl(PT_ETH));
+ eh = dp_packet_resize_l2(packet, ETH_HEADER_LEN);
+ eh->eth_dst = *dst;
+ eh->eth_src = *src;
+ eh->eth_type = pt_ns_type_be(packet->packet_type);
+ packet->packet_type = htonl(PT_ETH);
+}
+
+/* Removes Ethernet header, including VLAN header, from 'packet'.
+ *
+ * Previous to calling this function, 'ofpbuf_l3(packet)' must not be NULL */
+void
+pop_eth(struct dp_packet *packet)
+{
+ char *l2_5 = dp_packet_l2_5(packet);
+ char *l3 = dp_packet_l3(packet);
+ ovs_be16 ethertype;
+ int increment;
+
+ ovs_assert(packet->packet_type == htonl(PT_ETH));
+ ovs_assert(l3 != NULL);
+
+ if (l2_5) {
+ increment = packet->l2_5_ofs;
+ ethertype = *(ALIGNED_CAST(ovs_be16 *, (l2_5 - 2)));
+ } else {
+ increment = packet->l3_ofs;
+ ethertype = *(ALIGNED_CAST(ovs_be16 *, (l3 - 2)));
+ }
+
+ dp_packet_resize_l2(packet, -increment);
+ packet->packet_type = PACKET_TYPE_BE(OFPHTN_ETHERTYPE, ntohs(ethertype));
+}
+
/* Set ethertype of the packet. */
static void
set_ethertype(struct dp_packet *packet, ovs_be16 eth_type)
{
- struct eth_header *eh = dp_packet_l2(packet);
+ struct eth_header *eh = dp_packet_eth(packet);
if (!eh) {
return;
return inet_pton(AF_INET, s, ip) == 1;
}
+/* Parses string 's', which must be an IP address with a port number
+ * with ":" as a separator (e.g.: 192.168.1.2:80).
+ * Stores the IP address into '*ip' and port number to '*port'.
+ *
+ * Returns NULL if successful, otherwise an error message that the caller must
+ * free(). */
+char * OVS_WARN_UNUSED_RESULT
+ip_parse_port(const char *s, ovs_be32 *ip, ovs_be16 *port)
+{
+ int n = 0;
+ if (ovs_scan(s, IP_PORT_SCAN_FMT"%n", IP_PORT_SCAN_ARGS(ip, port), &n)
+ && !s[n]) {
+ return NULL;
+ }
+
+ return xasprintf("%s: invalid IP address or port number", s);
+}
+
/* Parses string 's', which must be an IP address with an optional netmask or
* CIDR prefix length. Stores the IP address into '*ip', netmask into '*mask',
* (255.255.255.255, if 's' lacks a netmask), and number of scanned characters
/* OK. */
} else if (ovs_scan_len(s, n, IP_SCAN_FMT"/%d",
IP_SCAN_ARGS(ip), &prefix)) {
- if (prefix <= 0 || prefix > 32) {
- return xasprintf("%s: network prefix bits not between 0 and "
- "32", s);
+ if (prefix < 0 || prefix > 32) {
+ return xasprintf("%s: IPv4 network prefix bits not between 0 and "
+ "32, inclusive", s);
}
*mask = be32_prefix_mask(prefix);
} else if (ovs_scan_len(s, n, IP_SCAN_FMT, IP_SCAN_ARGS(ip))) {
if (ovs_scan_len(s, n, " "IPV6_SCAN_FMT, ipv6_s)
&& ipv6_parse(ipv6_s, ip)) {
if (ovs_scan_len(s, n, "/%d", &prefix)) {
- if (prefix <= 0 || prefix > 128) {
+ if (prefix < 0 || prefix > 128) {
return xasprintf("%s: IPv6 network prefix bits not between 0 "
- "and 128", s);
+ "and 128, inclusive", s);
}
*mask = ipv6_create_mask(prefix);
} else if (ovs_scan_len(s, n, "/"IPV6_SCAN_FMT, ipv6_s)) {
}
}
-struct in6_addr ipv6_addr_bitand(const struct in6_addr *a,
- const struct in6_addr *b)
-{
- int i;
- struct in6_addr dst;
-
#ifdef s6_addr32
- for (i=0; i<4; i++) {
- dst.s6_addr32[i] = a->s6_addr32[i] & b->s6_addr32[i];
- }
+#define s6_addrX s6_addr32
+#define IPV6_FOR_EACH(VAR) for (int VAR = 0; VAR < 4; VAR++)
#else
- for (i=0; i<16; i++) {
- dst.s6_addr[i] = a->s6_addr[i] & b->s6_addr[i];
- }
+#define s6_addrX s6_addr
+#define IPV6_FOR_EACH(VAR) for (int VAR = 0; VAR < 16; VAR++)
#endif
- return dst;
+struct in6_addr
+ipv6_addr_bitand(const struct in6_addr *a, const struct in6_addr *b)
+{
+ struct in6_addr dst;
+ IPV6_FOR_EACH (i) {
+ dst.s6_addrX[i] = a->s6_addrX[i] & b->s6_addrX[i];
+ }
+ return dst;
+}
+
+struct in6_addr
+ipv6_addr_bitxor(const struct in6_addr *a, const struct in6_addr *b)
+{
+ struct in6_addr dst;
+ IPV6_FOR_EACH (i) {
+ dst.s6_addrX[i] = a->s6_addrX[i] ^ b->s6_addrX[i];
+ }
+ return dst;
+}
+
+bool
+ipv6_is_zero(const struct in6_addr *a)
+{
+ IPV6_FOR_EACH (i) {
+ if (a->s6_addrX[i]) {
+ return false;
+ }
+ }
+ return true;
}
/* Returns an in6_addr consisting of 'mask' high-order 1-bits and 128-N
eth->eth_src = eth_src;
eth->eth_type = htons(eth_type);
+ b->packet_type = htonl(PT_ETH);
dp_packet_reset_offsets(b);
dp_packet_set_l3(b, data);
return data;
}
-static void
+void
packet_set_ipv4_addr(struct dp_packet *packet,
ovs_16aligned_be32 *addr, ovs_be32 new_addr)
{
static void
packet_update_csum128(struct dp_packet *packet, uint8_t proto,
- ovs_16aligned_be32 addr[4], const ovs_be32 new_addr[4])
+ ovs_16aligned_be32 addr[4],
+ const struct in6_addr *new_addr)
{
size_t l4_size = dp_packet_l4_size(packet);
}
}
-static void
+void
packet_set_ipv6_addr(struct dp_packet *packet, uint8_t proto,
- ovs_16aligned_be32 addr[4], const ovs_be32 new_addr[4],
+ ovs_16aligned_be32 addr[4],
+ const struct in6_addr *new_addr,
bool recalculate_csum)
{
if (recalculate_csum) {
* appropriate. 'packet' must contain a valid IPv6 packet with correctly
* populated l[34] offsets. */
void
-packet_set_ipv6(struct dp_packet *packet, const ovs_be32 src[4],
- const ovs_be32 dst[4], uint8_t key_tc, ovs_be32 key_fl,
+packet_set_ipv6(struct dp_packet *packet, const struct in6_addr *src,
+ const struct in6_addr *dst, uint8_t key_tc, ovs_be32 key_fl,
uint8_t key_hl)
{
struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(packet);
}
void
-packet_set_nd(struct dp_packet *packet, const ovs_be32 target[4],
+packet_set_nd(struct dp_packet *packet, const struct in6_addr *target,
const struct eth_addr sll, const struct eth_addr tll)
{
struct ovs_nd_msg *ns;
- struct ovs_nd_opt *nd_opt;
+ struct ovs_nd_lla_opt *opt;
int bytes_remain = dp_packet_l4_size(packet);
if (OVS_UNLIKELY(bytes_remain < sizeof(*ns))) {
}
ns = dp_packet_l4(packet);
- nd_opt = &ns->options[0];
+ opt = &ns->options[0];
bytes_remain -= sizeof(*ns);
if (memcmp(&ns->target, target, sizeof(ovs_be32[4]))) {
- packet_set_ipv6_addr(packet, IPPROTO_ICMPV6,
- ns->target.be32,
- target, true);
+ packet_set_ipv6_addr(packet, IPPROTO_ICMPV6, ns->target.be32, target,
+ true);
}
- while (bytes_remain >= ND_OPT_LEN && nd_opt->nd_opt_len != 0) {
- if (nd_opt->nd_opt_type == ND_OPT_SOURCE_LINKADDR
- && nd_opt->nd_opt_len == 1) {
- if (!eth_addr_equals(nd_opt->nd_opt_mac, sll)) {
+ while (bytes_remain >= ND_LLA_OPT_LEN && opt->len != 0) {
+ if (opt->type == ND_OPT_SOURCE_LINKADDR && opt->len == 1) {
+ if (!eth_addr_equals(opt->mac, sll)) {
ovs_be16 *csum = &(ns->icmph.icmp6_cksum);
- *csum = recalc_csum48(*csum, nd_opt->nd_opt_mac, sll);
- nd_opt->nd_opt_mac = sll;
+ *csum = recalc_csum48(*csum, opt->mac, sll);
+ opt->mac = sll;
}
/* A packet can only contain one SLL or TLL option */
break;
- } else if (nd_opt->nd_opt_type == ND_OPT_TARGET_LINKADDR
- && nd_opt->nd_opt_len == 1) {
- if (!eth_addr_equals(nd_opt->nd_opt_mac, tll)) {
+ } else if (opt->type == ND_OPT_TARGET_LINKADDR && opt->len == 1) {
+ if (!eth_addr_equals(opt->mac, tll)) {
ovs_be16 *csum = &(ns->icmph.icmp6_cksum);
- *csum = recalc_csum48(*csum, nd_opt->nd_opt_mac, tll);
- nd_opt->nd_opt_mac = tll;
+ *csum = recalc_csum48(*csum, opt->mac, tll);
+ opt->mac = tll;
}
/* A packet can only contain one SLL or TLL option */
break;
}
- nd_opt += nd_opt->nd_opt_len;
- bytes_remain -= nd_opt->nd_opt_len * ND_OPT_LEN;
+ opt += opt->len;
+ bytes_remain -= opt->len * ND_LLA_OPT_LEN;
}
}
{
compose_arp__(b);
- struct eth_header *eth = dp_packet_l2(b);
+ struct eth_header *eth = dp_packet_eth(b);
eth->eth_dst = broadcast ? eth_addr_broadcast : arp_tha;
eth->eth_src = arp_sha;
dp_packet_reset_offsets(b);
dp_packet_set_l3(b, arp);
+
+ b->packet_type = htonl(PT_ETH);
}
-/* This function expect packet with ethernet header with correct
+/* This function expects packet with ethernet header with correct
* l3 pointer set. */
static void *
-compose_ipv6(struct dp_packet *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, int size)
+compose_ipv6(struct dp_packet *packet, uint8_t proto,
+ const struct in6_addr *src, const struct in6_addr *dst,
+ uint8_t key_tc, ovs_be32 key_fl, uint8_t key_hl, int size)
{
struct ip6_hdr *nh;
void *data;
return data;
}
+/* Compose an IPv6 Neighbor Discovery Neighbor Solicitation message. */
void
-compose_nd(struct dp_packet *b, const struct eth_addr eth_src,
- struct in6_addr *ipv6_src, struct in6_addr *ipv6_dst)
+compose_nd_ns(struct dp_packet *b, const struct eth_addr eth_src,
+ const struct in6_addr *ipv6_src, const 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;
+ struct ovs_nd_lla_opt *lla_opt;
uint32_t icmp_csum;
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);
- ns = compose_ipv6(b, IPPROTO_ICMPV6,
- ALIGNED_CAST(ovs_be32 *, ipv6_src->s6_addr),
- ALIGNED_CAST(ovs_be32 *, sn_addr.s6_addr),
- 0, 0, 255,
- ND_MSG_LEN + ND_OPT_LEN);
+ ns = compose_ipv6(b, IPPROTO_ICMPV6, ipv6_src, &sn_addr,
+ 0, 0, 255, ND_MSG_LEN + ND_LLA_OPT_LEN);
ns->icmph.icmp6_type = ND_NEIGHBOR_SOLICIT;
ns->icmph.icmp6_code = 0;
- put_16aligned_be32(&ns->rco_flags, htonl(0));
+ put_16aligned_be32(&ns->rso_flags, htonl(0));
+
+ lla_opt = &ns->options[0];
+ lla_opt->type = ND_OPT_SOURCE_LINKADDR;
+ lla_opt->len = 1;
- nd_opt = &ns->options[0];
- nd_opt->nd_opt_type = ND_OPT_SOURCE_LINKADDR;
- nd_opt->nd_opt_len = 1;
+ packet_set_nd(b, ipv6_dst, eth_src, eth_addr_zero);
- packet_set_nd(b, ALIGNED_CAST(ovs_be32 *, ipv6_dst->s6_addr),
- eth_src, eth_addr_zero);
ns->icmph.icmp6_cksum = 0;
icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
- ns->icmph.icmp6_cksum = csum_finish(csum_continue(icmp_csum, ns,
- ND_MSG_LEN + ND_OPT_LEN));
+ ns->icmph.icmp6_cksum = csum_finish(
+ csum_continue(icmp_csum, ns, ND_MSG_LEN + ND_LLA_OPT_LEN));
+}
+
+/* Compose an IPv6 Neighbor Discovery Neighbor Advertisement message. */
+void
+compose_nd_na(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,
+ ovs_be32 rso_flags)
+{
+ struct ovs_nd_msg *na;
+ struct ovs_nd_lla_opt *lla_opt;
+ uint32_t icmp_csum;
+
+ eth_compose(b, eth_dst, eth_src, ETH_TYPE_IPV6, IPV6_HEADER_LEN);
+ na = compose_ipv6(b, IPPROTO_ICMPV6, ipv6_src, ipv6_dst,
+ 0, 0, 255, ND_MSG_LEN + ND_LLA_OPT_LEN);
+
+ na->icmph.icmp6_type = ND_NEIGHBOR_ADVERT;
+ na->icmph.icmp6_code = 0;
+ put_16aligned_be32(&na->rso_flags, rso_flags);
+
+ lla_opt = &na->options[0];
+ lla_opt->type = ND_OPT_TARGET_LINKADDR;
+ lla_opt->len = 1;
+
+ packet_set_nd(b, ipv6_src, eth_addr_zero, eth_src);
+
+ na->icmph.icmp6_cksum = 0;
+ icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
+ na->icmph.icmp6_cksum = csum_finish(csum_continue(
+ icmp_csum, na, ND_MSG_LEN + ND_LLA_OPT_LEN));
+}
+
+/* Compose an IPv6 Neighbor Discovery Router Advertisement message with
+ * Source Link-layer Address Option and MTU Option.
+ * Caller can call packet_put_ra_prefix_opt to append Prefix Information
+ * Options to composed messags in 'b'. */
+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_lt, ovs_be32 reachable_time,
+ ovs_be32 retrans_timer, ovs_be32 mtu)
+{
+ /* Don't compose Router Advertisement packet with MTU Option if mtu
+ * value is 0. */
+ bool with_mtu = mtu != 0;
+ size_t mtu_opt_len = with_mtu ? ND_MTU_OPT_LEN : 0;
+
+ eth_compose(b, eth_dst, eth_src, ETH_TYPE_IPV6, IPV6_HEADER_LEN);
+
+ struct ovs_ra_msg *ra = compose_ipv6(
+ b, IPPROTO_ICMPV6, ipv6_src, ipv6_dst, 0, 0, 255,
+ RA_MSG_LEN + ND_LLA_OPT_LEN + mtu_opt_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_lt;
+ ra->reachable_time = reachable_time;
+ ra->retrans_timer = retrans_timer;
+
+ struct ovs_nd_lla_opt *lla_opt = ra->options;
+ lla_opt->type = ND_OPT_SOURCE_LINKADDR;
+ lla_opt->len = 1;
+ lla_opt->mac = eth_src;
+
+ if (with_mtu) {
+ /* ovs_nd_mtu_opt has the same size with ovs_nd_lla_opt. */
+ struct ovs_nd_mtu_opt *mtu_opt
+ = (struct ovs_nd_mtu_opt *)(lla_opt + 1);
+ mtu_opt->type = ND_OPT_MTU;
+ mtu_opt->len = 1;
+ mtu_opt->reserved = 0;
+ put_16aligned_be32(&mtu_opt->mtu, mtu);
+ }
+
+ ra->icmph.icmp6_cksum = 0;
+ uint32_t icmp_csum = packet_csum_pseudoheader6(dp_packet_l3(b));
+ ra->icmph.icmp6_cksum = csum_finish(csum_continue(
+ icmp_csum, ra, RA_MSG_LEN + ND_LLA_OPT_LEN + mtu_opt_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_be128 prefix)
+{
+ 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 =
+ dp_packet_put_uninit(b, sizeof *prefix_opt);
+ prefix_opt->type = ND_OPT_PREFIX_INFORMATION;
+ prefix_opt->len = 4;
+ prefix_opt->prefix_len = plen;
+ prefix_opt->la_flags = la_flags;
+ put_16aligned_be32(&prefix_opt->valid_lifetime, valid_lifetime);
+ put_16aligned_be32(&prefix_opt->preferred_lifetime, preferred_lifetime);
+ put_16aligned_be32(&prefix_opt->reserved, 0);
+ memcpy(prefix_opt->prefix.be32, prefix.be32, sizeof(ovs_be32[4]));
+
+ ra->icmph.icmp6_cksum = 0;
+ uint32_t 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));
}
uint32_t
{
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_continue(partial, &ip6->ip6_src, sizeof ip6->ip6_src);
+ partial = csum_continue(partial, &ip6->ip6_dst, sizeof ip6->ip6_dst);
partial = csum_add16(partial, htons(ip6->ip6_nxt));
partial = csum_add16(partial, ip6->ip6_plen);
return partial;
}
+
+/* Calculate the IPv6 upper layer checksum according to RFC2460. We pass the
+ ip6_nxt and ip6_plen values, so it will also work if extension headers
+ are present. */
+uint16_t
+packet_csum_upperlayer6(const struct ovs_16aligned_ip6_hdr *ip6,
+ const void *data, uint8_t l4_protocol,
+ uint16_t l4_size)
+{
+ uint32_t partial = 0;
+
+ partial = csum_continue(partial, &ip6->ip6_src, sizeof ip6->ip6_src);
+ partial = csum_continue(partial, &ip6->ip6_dst, sizeof ip6->ip6_dst);
+ partial = csum_add16(partial, htons(l4_protocol));
+ partial = csum_add16(partial, htons(l4_size));
+
+ partial = csum_continue(partial, data, l4_size);
+
+ return csum_finish(partial);
+}
#endif
+
+void
+IP_ECN_set_ce(struct dp_packet *pkt, bool is_ipv6)
+{
+ if (is_ipv6) {
+ ovs_16aligned_be32 *ip6 = dp_packet_l3(pkt);
+
+ put_16aligned_be32(ip6, get_16aligned_be32(ip6) |
+ htonl(IP_ECN_CE << 20));
+ } else {
+ struct ip_header *nh = dp_packet_l3(pkt);
+ uint8_t tos = nh->ip_tos;
+
+ tos |= IP_ECN_CE;
+ if (nh->ip_tos != tos) {
+ nh->ip_csum = recalc_csum16(nh->ip_csum, htons(nh->ip_tos),
+ htons((uint16_t) tos));
+ nh->ip_tos = tos;
+ }
+ }
+}