/*
- * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 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 "unaligned.h"
const struct in6_addr in6addr_exact = IN6ADDR_EXACT_INIT;
+const struct in6_addr in6addr_all_hosts = IN6ADDR_ALL_HOSTS_INIT;
/* 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
* 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])
+eth_addr_is_reserved(const struct eth_addr ea)
{
struct eth_addr_node {
struct hmap_node hmap_node;
}
bool
-eth_addr_from_string(const char *s, uint8_t ea[ETH_ADDR_LEN])
+eth_addr_from_string(const char *s, struct eth_addr *ea)
{
- if (ovs_scan(s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(ea))) {
+ if (ovs_scan(s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(*ea))) {
return true;
} else {
- memset(ea, 0, ETH_ADDR_LEN);
+ *ea = eth_addr_zero;
return false;
}
}
* The returned packet has enough headroom to insert an 802.1Q VLAN header if
* desired. */
void
-compose_rarp(struct dp_packet *b, const uint8_t eth_src[ETH_ADDR_LEN])
+compose_rarp(struct dp_packet *b, const struct eth_addr eth_src)
{
struct eth_header *eth;
struct arp_eth_header *arp;
+ ARP_ETH_HEADER_LEN);
dp_packet_reserve(b, 2 + VLAN_HEADER_LEN);
eth = dp_packet_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_dst = eth_addr_broadcast;
+ eth->eth_src = eth_src;
eth->eth_type = htons(ETH_TYPE_RARP);
arp = dp_packet_put_uninit(b, sizeof *arp);
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_sha = eth_src;
put_16aligned_be32(&arp->ar_spa, htonl(0));
- memcpy(arp->ar_tha, eth_src, ETH_ADDR_LEN);
+ arp->ar_tha = eth_src;
put_16aligned_be32(&arp->ar_tpa, htonl(0));
- dp_packet_set_frame(b, eth);
+ dp_packet_reset_offsets(b);
dp_packet_set_l3(b, arp);
}
/* Removes outermost VLAN header (if any is present) from 'packet'.
*
- * 'packet->l2_5' should initially point to 'packet''s outer-most MPLS header
- * or may be NULL if there are no MPLS headers. */
+ * 'packet->l2_5' should initially point to 'packet''s outer-most VLAN header
+ * or may be NULL if there are no VLAN headers. */
void
eth_pop_vlan(struct dp_packet *packet)
{
struct vlan_eth_header *veh = dp_packet_l2(packet);
if (veh && dp_packet_size(packet) >= sizeof *veh
- && veh->veth_type == htons(ETH_TYPE_VLAN)) {
+ && eth_type_vlan(veh->veth_type)) {
memmove((char *)veh + VLAN_HEADER_LEN, veh, 2 * ETH_ADDR_LEN);
dp_packet_resize_l2(packet, -VLAN_HEADER_LEN);
return;
}
- if (eh->eth_type == htons(ETH_TYPE_VLAN)) {
+ if (eth_type_vlan(eh->eth_type)) {
ovs_be16 *p;
char *l2_5 = dp_packet_l2_5(packet);
}
}
-/* Push MPLS label stack entry 'lse' onto 'packet' as the the outermost MPLS
+/* Push MPLS label stack entry 'lse' onto 'packet' as the outermost MPLS
* header. If 'packet' does not already have any MPLS labels, then its
* Ethertype is changed to 'ethtype' (which must be an MPLS Ethertype). */
void
}
void
-eth_format_masked(const uint8_t eth[ETH_ADDR_LEN],
- const uint8_t mask[ETH_ADDR_LEN], struct ds *s)
+eth_format_masked(const struct eth_addr eth,
+ const struct eth_addr *mask, struct ds *s)
{
ds_put_format(s, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth));
- if (mask && !eth_mask_is_exact(mask)) {
- ds_put_format(s, "/"ETH_ADDR_FMT, ETH_ADDR_ARGS(mask));
- }
-}
-
-void
-eth_addr_bitand(const uint8_t src[ETH_ADDR_LEN],
- const uint8_t mask[ETH_ADDR_LEN],
- uint8_t dst[ETH_ADDR_LEN])
-{
- int i;
-
- for (i = 0; i < ETH_ADDR_LEN; i++) {
- dst[i] = src[i] & mask[i];
+ if (mask && !eth_mask_is_exact(*mask)) {
+ ds_put_format(s, "/"ETH_ADDR_FMT, ETH_ADDR_ARGS(*mask));
}
}
}
}
-
-/* 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_masked(struct ds *s, const struct in6_addr *addr,
- const struct in6_addr *mask)
+ipv6_format_mapped(const struct in6_addr *addr, struct ds *s)
{
- print_ipv6_addr(s, addr);
+ 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 {
+ ipv6_format_addr(addr, s);
+ }
+}
+
+void
+ipv6_format_masked(const struct in6_addr *addr, const struct in6_addr *mask,
+ struct ds *s)
+{
+ 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);
}
}
}
* The returned packet has enough headroom to insert an 802.1Q VLAN header if
* desired. */
void *
-eth_compose(struct dp_packet *b, const uint8_t eth_dst[ETH_ADDR_LEN],
- const uint8_t eth_src[ETH_ADDR_LEN], uint16_t eth_type,
+eth_compose(struct dp_packet *b, const struct eth_addr eth_dst,
+ const struct eth_addr eth_src, uint16_t eth_type,
size_t size)
{
void *data;
eth = dp_packet_put_uninit(b, ETH_HEADER_LEN);
data = dp_packet_put_uninit(b, size);
- memcpy(eth->eth_dst, eth_dst, ETH_ADDR_LEN);
- memcpy(eth->eth_src, eth_src, ETH_ADDR_LEN);
+ eth->eth_dst = eth_dst;
+ eth->eth_src = eth_src;
eth->eth_type = htons(eth_type);
- dp_packet_set_frame(b, eth);
+ dp_packet_reset_offsets(b);
dp_packet_set_l3(b, data);
return data;
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 uint8_t sll[ETH_ADDR_LEN],
- const uint8_t tll[ETH_ADDR_LEN]) {
+ const struct eth_addr sll, const struct eth_addr tll) {
struct ovs_nd_msg *ns;
struct ovs_nd_opt *nd_opt;
int bytes_remain = dp_packet_l4_size(packet);
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 (memcmp(nd_opt->nd_opt_data, sll, ETH_ADDR_LEN)) {
+ if (!eth_addr_equals(nd_opt->nd_opt_mac, sll)) {
ovs_be16 *csum = &(ns->icmph.icmp6_cksum);
- *csum = recalc_csum48(*csum, nd_opt->nd_opt_data, sll);
- memcpy(nd_opt->nd_opt_data, sll, ETH_ADDR_LEN);
+ *csum = recalc_csum48(*csum, nd_opt->nd_opt_mac, sll);
+ nd_opt->nd_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 (memcmp(nd_opt->nd_opt_data, tll, ETH_ADDR_LEN)) {
+ if (!eth_addr_equals(nd_opt->nd_opt_mac, tll)) {
ovs_be16 *csum = &(ns->icmph.icmp6_cksum);
- *csum = recalc_csum48(*csum, nd_opt->nd_opt_data, tll);
- memcpy(nd_opt->nd_opt_data, tll, ETH_ADDR_LEN);
+ *csum = recalc_csum48(*csum, nd_opt->nd_opt_mac, tll);
+ nd_opt->nd_opt_mac = tll;
}
/* A packet can only contain one SLL or TLL option */
#define ARP_PACKET_SIZE (2 + ETH_HEADER_LEN + VLAN_HEADER_LEN + \
ARP_ETH_HEADER_LEN)
+/* Clears 'b' and replaces its contents by an ARP frame with the specified
+ * 'arp_op', 'arp_sha', 'arp_tha', 'arp_spa', and 'arp_tpa'. The outer
+ * Ethernet frame is initialized with Ethernet source 'arp_sha' and destination
+ * 'arp_tha', except that destination ff:ff:ff:ff:ff:ff is used instead if
+ * 'broadcast' is true. */
void
-compose_arp(struct dp_packet *b, const uint8_t eth_src[ETH_ADDR_LEN],
- ovs_be32 ip_src, ovs_be32 ip_dst)
+compose_arp(struct dp_packet *b, uint16_t arp_op,
+ const struct eth_addr arp_sha, const struct eth_addr arp_tha,
+ bool broadcast, ovs_be32 arp_spa, ovs_be32 arp_tpa)
{
struct eth_header *eth;
struct arp_eth_header *arp;
dp_packet_reserve(b, 2 + VLAN_HEADER_LEN);
eth = dp_packet_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_dst = broadcast ? eth_addr_broadcast : arp_tha;
+ eth->eth_src = arp_sha;
eth->eth_type = htons(ETH_TYPE_ARP);
arp = dp_packet_put_uninit(b, sizeof *arp);
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_REQUEST);
- memcpy(arp->ar_sha, eth_src, ETH_ADDR_LEN);
- memset(arp->ar_tha, 0, ETH_ADDR_LEN);
+ arp->ar_op = htons(arp_op);
+ arp->ar_sha = arp_sha;
+ arp->ar_tha = arp_tha;
- put_16aligned_be32(&arp->ar_spa, ip_src);
- put_16aligned_be32(&arp->ar_tpa, ip_dst);
+ put_16aligned_be32(&arp->ar_spa, arp_spa);
+ put_16aligned_be32(&arp->ar_tpa, arp_tpa);
- dp_packet_set_frame(b, eth);
+ dp_packet_reset_offsets(b);
dp_packet_set_l3(b, arp);
}
+
+uint32_t
+packet_csum_pseudoheader(const struct ip_header *ip)
+{
+ uint32_t partial = 0;
+
+ partial = csum_add32(partial, get_16aligned_be32(&ip->ip_src));
+ partial = csum_add32(partial, get_16aligned_be32(&ip->ip_dst));
+ partial = csum_add16(partial, htons(ip->ip_proto));
+ partial = csum_add16(partial, htons(ntohs(ip->ip_tot_len) -
+ IP_IHL(ip->ip_ihl_ver) * 4));
+
+ return partial;
+}
+