#include <netinet/icmp6.h>
#include <stdlib.h>
#include <string.h>
+
#include "byte-order.h"
#include "coverage.h"
#include "dpif.h"
#include "packets.h"
#include "simap.h"
#include "timeval.h"
+#include "unaligned.h"
#include "util.h"
-#include "vlog.h"
+#include "openvswitch/vlog.h"
VLOG_DEFINE_THIS_MODULE(odp_util);
switch ((enum ovs_action_attr) type) {
case OVS_ACTION_ATTR_OUTPUT: return sizeof(uint32_t);
+ case OVS_ACTION_ATTR_TUNNEL_PUSH: return -2;
+ case OVS_ACTION_ATTR_TUNNEL_POP: return sizeof(uint32_t);
case OVS_ACTION_ATTR_USERSPACE: return -2;
case OVS_ACTION_ATTR_PUSH_VLAN: return sizeof(struct ovs_action_push_vlan);
case OVS_ACTION_ATTR_POP_VLAN: return 0;
ds_put_format(ds, ")");
}
+static void
+format_odp_tnl_push_header(struct ds *ds, struct ovs_action_push_tnl *data)
+{
+ const struct eth_header *eth;
+ const struct ip_header *ip;
+ const void *l3;
+
+ eth = (const struct eth_header *)data->header;
+
+ l3 = eth + 1;
+ ip = (const struct ip_header *)l3;
+
+ /* Ethernet */
+ ds_put_format(ds, "header(size=%"PRIu8",type=%"PRIu8",eth(dst=",
+ data->header_len, data->tnl_type);
+ ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_dst));
+ ds_put_format(ds, ",src=");
+ ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(eth->eth_src));
+ ds_put_format(ds, ",dl_type=0x%04"PRIx16"),", ntohs(eth->eth_type));
+
+ /* IPv4 */
+ ds_put_format(ds, "ipv4(src="IP_FMT",dst="IP_FMT",proto=%"PRIu8
+ ",tos=%#"PRIx8",ttl=%"PRIu8",frag=0x%"PRIx16"),",
+ IP_ARGS(get_16aligned_be32(&ip->ip_src)),
+ IP_ARGS(get_16aligned_be32(&ip->ip_dst)),
+ ip->ip_proto, ip->ip_tos,
+ ip->ip_ttl,
+ ip->ip_frag_off);
+
+ if (data->tnl_type == OVS_VPORT_TYPE_VXLAN) {
+ const struct vxlanhdr *vxh;
+ const struct udp_header *udp;
+
+ /* UDP */
+ udp = (const struct udp_header *) (ip + 1);
+ ds_put_format(ds, "udp(src=%"PRIu16",dst=%"PRIu16"),",
+ ntohs(udp->udp_src), ntohs(udp->udp_dst));
+
+ /* VxLan */
+ vxh = (const struct vxlanhdr *) (udp + 1);
+ ds_put_format(ds, "vxlan(flags=0x%"PRIx32",vni=0x%"PRIx32")",
+ ntohl(get_16aligned_be32(&vxh->vx_flags)),
+ ntohl(get_16aligned_be32(&vxh->vx_vni)));
+ } else if (data->tnl_type == OVS_VPORT_TYPE_GRE) {
+ const struct gre_base_hdr *greh;
+ ovs_16aligned_be32 *options;
+ void *l4;
+
+ l4 = ((uint8_t *)l3 + sizeof(struct ip_header));
+ greh = (const struct gre_base_hdr *) l4;
+
+ ds_put_format(ds, "gre((flags=0x%"PRIx16",proto=0x%"PRIx16")",
+ greh->flags, ntohs(greh->protocol));
+ options = (ovs_16aligned_be32 *)(greh + 1);
+ if (greh->flags & htons(GRE_CSUM)) {
+ ds_put_format(ds, ",csum=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
+ options++;
+ }
+ if (greh->flags & htons(GRE_KEY)) {
+ ds_put_format(ds, ",key=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
+ options++;
+ }
+ if (greh->flags & htons(GRE_SEQ)) {
+ ds_put_format(ds, ",seq=0x%"PRIx32, ntohl(get_16aligned_be32(options)));
+ options++;
+ }
+ ds_put_format(ds, ")");
+ }
+ ds_put_format(ds, ")");
+}
+
+static void
+format_odp_tnl_push_action(struct ds *ds, const struct nlattr *attr)
+{
+ struct ovs_action_push_tnl *data;
+
+ data = (struct ovs_action_push_tnl *) nl_attr_get(attr);
+
+ ds_put_format(ds, "tnl_push(tnl_port(%"PRIu32"),", data->tnl_port);
+ format_odp_tnl_push_header(ds, data);
+ ds_put_format(ds, ",out_port(%"PRIu32"))", data->out_port);
+}
+
static void
format_odp_action(struct ds *ds, const struct nlattr *a)
{
case OVS_ACTION_ATTR_OUTPUT:
ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a));
break;
+ case OVS_ACTION_ATTR_TUNNEL_POP:
+ ds_put_format(ds, "tnl_pop(%"PRIu32")", nl_attr_get_u32(a));
+ break;
+ case OVS_ACTION_ATTR_TUNNEL_PUSH:
+ format_odp_tnl_push_action(ds, a);
+ break;
case OVS_ACTION_ATTR_USERSPACE:
format_odp_userspace_action(ds, a);
break;
return -EINVAL;
}
+static int
+ovs_parse_tnl_push(const char *s, struct ovs_action_push_tnl *data)
+{
+ struct eth_header *eth;
+ struct ip_header *ip;
+ struct udp_header *udp;
+ struct gre_base_hdr *greh;
+ uint16_t gre_proto, dl_type, udp_src, udp_dst;
+ ovs_be32 sip, dip;
+ uint32_t tnl_type = 0, header_len = 0;
+ void *l3, *l4;
+ int n = 0;
+
+ if (!ovs_scan_len(s, &n, "tnl_push(tnl_port(%"SCNi32"),", &data->tnl_port)) {
+ return -EINVAL;
+ }
+ eth = (struct eth_header *) data->header;
+ l3 = (data->header + sizeof *eth);
+ l4 = ((uint8_t *) l3 + sizeof (struct ip_header));
+ ip = (struct ip_header *) l3;
+ if (!ovs_scan_len(s, &n, "header(size=%"SCNi32",type=%"SCNi32","
+ "eth(dst="ETH_ADDR_SCAN_FMT",",
+ &data->header_len,
+ &data->tnl_type,
+ ETH_ADDR_SCAN_ARGS(eth->eth_dst))) {
+ return -EINVAL;
+ }
+
+ if (!ovs_scan_len(s, &n, "src="ETH_ADDR_SCAN_FMT",",
+ ETH_ADDR_SCAN_ARGS(eth->eth_src))) {
+ return -EINVAL;
+ }
+ if (!ovs_scan_len(s, &n, "dl_type=0x%"SCNx16"),", &dl_type)) {
+ return -EINVAL;
+ }
+ eth->eth_type = htons(dl_type);
+
+ /* IPv4 */
+ if (!ovs_scan_len(s, &n, "ipv4(src="IP_SCAN_FMT",dst="IP_SCAN_FMT",proto=%"SCNi8
+ ",tos=%"SCNi8",ttl=%"SCNi8",frag=0x%"SCNx16"),",
+ IP_SCAN_ARGS(&sip),
+ IP_SCAN_ARGS(&dip),
+ &ip->ip_proto, &ip->ip_tos,
+ &ip->ip_ttl, &ip->ip_frag_off)) {
+ return -EINVAL;
+ }
+ put_16aligned_be32(&ip->ip_src, sip);
+ put_16aligned_be32(&ip->ip_dst, dip);
+
+ /* Tunnel header */
+ udp = (struct udp_header *) l4;
+ greh = (struct gre_base_hdr *) l4;
+ if (ovs_scan_len(s, &n, "udp(src=%"SCNi16",dst=%"SCNi16"),",
+ &udp_src, &udp_dst)) {
+ struct vxlanhdr *vxh;
+ uint32_t vx_flags, vx_vni;
+
+ udp->udp_src = htons(udp_src);
+ udp->udp_dst = htons(udp_dst);
+ udp->udp_len = 0;
+ udp->udp_csum = 0;
+
+ vxh = (struct vxlanhdr *) (udp + 1);
+ if (!ovs_scan_len(s, &n, "vxlan(flags=0x%"SCNx32",vni=0x%"SCNx32"))",
+ &vx_flags, &vx_vni)) {
+ return -EINVAL;
+ }
+ put_16aligned_be32(&vxh->vx_flags, htonl(vx_flags));
+ put_16aligned_be32(&vxh->vx_vni, htonl(vx_vni));
+ tnl_type = OVS_VPORT_TYPE_VXLAN;
+ header_len = sizeof *eth + sizeof *ip +
+ sizeof *udp + sizeof *vxh;
+ } else if (ovs_scan_len(s, &n, "gre((flags=0x%"SCNx16",proto=0x%"SCNx16")",
+ &greh->flags, &gre_proto)){
+
+ tnl_type = OVS_VPORT_TYPE_GRE;
+ greh->protocol = htons(gre_proto);
+ ovs_16aligned_be32 *options = (ovs_16aligned_be32 *) (greh + 1);
+
+ if (greh->flags & htons(GRE_CSUM)) {
+ uint32_t csum;
+
+ if (!ovs_scan_len(s, &n, ",csum=0x%"SCNx32, &csum)) {
+ return -EINVAL;
+ }
+ put_16aligned_be32(options, htonl(csum));
+ options++;
+ }
+ if (greh->flags & htons(GRE_KEY)) {
+ uint32_t key;
+
+ if (!ovs_scan_len(s, &n, ",key=0x%"SCNx32, &key)) {
+ return -EINVAL;
+ }
+
+ put_16aligned_be32(options, htonl(key));
+ options++;
+ }
+ if (greh->flags & htons(GRE_SEQ)) {
+ uint32_t seq;
+
+ if (!ovs_scan_len(s, &n, ",seq=0x%"SCNx32, &seq)) {
+ return -EINVAL;
+ }
+ put_16aligned_be32(options, htonl(seq));
+ options++;
+ }
+
+ if (!ovs_scan_len(s, &n, "))")) {
+ return -EINVAL;
+ }
+
+ header_len = sizeof *eth + sizeof *ip +
+ ((uint8_t *) options - (uint8_t *) greh);
+ } else {
+ return -EINVAL;
+ }
+
+ /* check tunnel meta data. */
+ if (data->tnl_type != tnl_type) {
+ return -EINVAL;
+ }
+ if (data->header_len != header_len) {
+ return -EINVAL;
+ }
+
+ /* Out port */
+ if (!ovs_scan_len(s, &n, ",out_port(%"SCNi32"))", &data->out_port)) {
+ return -EINVAL;
+ }
+
+ return n;
+}
+
static int
parse_odp_action(const char *s, const struct simap *port_names,
struct ofpbuf *actions)
}
}
+ {
+ uint32_t recirc_id;
+ int n = -1;
+
+ if (ovs_scan(s, "recirc(%"PRIu32")%n", &recirc_id, &n)) {
+ nl_msg_put_u32(actions, OVS_ACTION_ATTR_RECIRC, recirc_id);
+ return n;
+ }
+ }
+
if (!strncmp(s, "userspace(", 10)) {
return parse_odp_userspace_action(s, actions);
}
}
}
+ {
+ uint32_t port;
+ int n;
+
+ if (ovs_scan(s, "tnl_pop(%"SCNi32")%n", &port, &n)) {
+ nl_msg_put_u32(actions, OVS_ACTION_ATTR_TUNNEL_POP, port);
+ return n;
+ }
+ }
+
+ {
+ struct ovs_action_push_tnl data;
+ int n;
+
+ n = ovs_parse_tnl_push(s, &data);
+ if (n > 0) {
+ odp_put_tnl_push_action(actions, &data);
+ return n;
+ } else if (n < 0) {
+ return n;
+ }
+ }
return -EINVAL;
}
return ofpbuf_base(ofp);
}
+int
+odp_ufid_from_string(const char *s_, ovs_u128 *ufid)
+{
+ const char *s = s_;
+
+ if (ovs_scan(s, "ufid:")) {
+ size_t n;
+
+ s += 5;
+ if (ovs_scan(s, "0x")) {
+ s += 2;
+ }
+
+ n = strspn(s, "0123456789abcdefABCDEF");
+ if (n != 32) {
+ return -EINVAL;
+ }
+
+ if (!ovs_scan(s, "%16"SCNx64"%16"SCNx64, &ufid->u64.hi,
+ &ufid->u64.lo)) {
+ return -EINVAL;
+ }
+ s += n;
+ s += strspn(s, delimiters);
+
+ return s - s_;
+ }
+
+ return 0;
+}
+
+void
+odp_format_ufid(const ovs_u128 *ufid, struct ds *ds)
+{
+ ds_put_format(ds, "ufid:%016"PRIx64"%016"PRIx64, ufid->u64.hi,
+ ufid->u64.lo);
+}
+
/* Appends to 'ds' a string representation of the 'key_len' bytes of
* OVS_KEY_ATTR_* attributes in 'key'. If non-null, additionally formats the
* 'mask_len' bytes of 'mask' which apply to 'key'. If 'portno_names' is
for (;;) {
int retval;
- s += strspn(s, ", \t\r\n");
+ s += strspn(s, delimiters);
if (!*s) {
return -EINVAL;
} else if (*s == ')') {
bool is_mask);
static void get_arp_key(const struct flow *, struct ovs_key_arp *);
static void put_arp_key(const struct ovs_key_arp *, struct flow *);
+static void get_nd_key(const struct flow *, struct ovs_key_nd *);
+static void put_nd_key(const struct ovs_key_nd *, struct flow *);
/* These share the same layout. */
union ovs_key_tp {
enum ovs_key_attr expected_bit = 0xff;
if (eth_type_mpls(src_flow->dl_type)) {
- size_t size = nl_attr_get_size(attrs[OVS_KEY_ATTR_MPLS]);
- const ovs_be32 *mpls_lse = nl_attr_get(attrs[OVS_KEY_ATTR_MPLS]);
- int n = size / sizeof(ovs_be32);
- int i;
-
- if (!size || size % sizeof(ovs_be32)) {
- return ODP_FIT_ERROR;
- }
-
- if (!is_mask) {
+ if (!is_mask || present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) {
expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
+ }
+ if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) {
+ size_t size = nl_attr_get_size(attrs[OVS_KEY_ATTR_MPLS]);
+ const ovs_be32 *mpls_lse = nl_attr_get(attrs[OVS_KEY_ATTR_MPLS]);
+ int n = size / sizeof(ovs_be32);
+ int i;
- if (!(present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS))) {
- return ODP_FIT_TOO_LITTLE;
+ if (!size || size % sizeof(ovs_be32)) {
+ return ODP_FIT_ERROR;
}
- } else if (present_attrs & (UINT64_C(1) << OVS_KEY_ATTR_MPLS)) {
if (flow->mpls_lse[0] && flow->dl_type != htons(0xffff)) {
return ODP_FIT_ERROR;
}
- expected_attrs |= (UINT64_C(1) << OVS_KEY_ATTR_MPLS);
- }
- for (i = 0; i < n && i < FLOW_MAX_MPLS_LABELS; i++) {
- flow->mpls_lse[i] = mpls_lse[i];
- }
- if (n > FLOW_MAX_MPLS_LABELS) {
- return ODP_FIT_TOO_MUCH;
- }
+ for (i = 0; i < n && i < FLOW_MAX_MPLS_LABELS; i++) {
+ flow->mpls_lse[i] = mpls_lse[i];
+ }
+ if (n > FLOW_MAX_MPLS_LABELS) {
+ return ODP_FIT_TOO_MUCH;
+ }
- if (!is_mask) {
- /* BOS may be set only in the innermost label. */
- for (i = 0; i < n - 1; i++) {
- if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) {
- return ODP_FIT_ERROR;
+ if (!is_mask) {
+ /* BOS may be set only in the innermost label. */
+ for (i = 0; i < n - 1; i++) {
+ if (flow->mpls_lse[i] & htonl(MPLS_BOS_MASK)) {
+ return ODP_FIT_ERROR;
+ }
}
- }
- /* BOS must be set in the innermost label. */
- if (n < FLOW_MAX_MPLS_LABELS
- && !(flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK))) {
- return ODP_FIT_TOO_LITTLE;
+ /* BOS must be set in the innermost label. */
+ if (n < FLOW_MAX_MPLS_LABELS
+ && !(flow->mpls_lse[n - 1] & htonl(MPLS_BOS_MASK))) {
+ return ODP_FIT_TOO_LITTLE;
+ }
}
}
tun_key_to_attr(odp_actions, tunnel);
nl_msg_end_nested(odp_actions, offset);
}
+
+void
+odp_put_tnl_push_action(struct ofpbuf *odp_actions,
+ struct ovs_action_push_tnl *data)
+{
+ int size = offsetof(struct ovs_action_push_tnl, header);
+
+ size += data->header_len;
+ nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_TUNNEL_PUSH, data, size);
+}
+
\f
/* The commit_odp_actions() function and its helpers. */
base->vlan_tci = vlan_tci;
}
+/* Wildcarding already done at action translation time. */
static void
commit_mpls_action(const struct flow *flow, struct flow *base,
- struct ofpbuf *odp_actions, struct flow_wildcards *wc)
+ struct ofpbuf *odp_actions)
{
- int base_n = flow_count_mpls_labels(base, wc);
- int flow_n = flow_count_mpls_labels(flow, wc);
+ int base_n = flow_count_mpls_labels(base, NULL);
+ int flow_n = flow_count_mpls_labels(flow, NULL);
int common_n = flow_count_common_mpls_labels(flow, flow_n, base, base_n,
- wc);
+ NULL);
while (base_n > common_n) {
if (base_n - 1 == common_n && flow_n > common_n) {
dl_type = flow->dl_type;
}
nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_POP_MPLS, dl_type);
- popped = flow_pop_mpls(base, base_n, flow->dl_type, wc);
+ popped = flow_pop_mpls(base, base_n, flow->dl_type, NULL);
ovs_assert(popped);
base_n--;
}
sizeof *mpls);
mpls->mpls_ethertype = flow->dl_type;
mpls->mpls_lse = flow->mpls_lse[flow_n - base_n - 1];
- flow_push_mpls(base, base_n, mpls->mpls_ethertype, wc);
+ flow_push_mpls(base, base_n, mpls->mpls_ethertype, NULL);
flow_set_mpls_lse(base, 0, mpls->mpls_lse);
base_n++;
}
return 0;
}
+static void
+get_nd_key(const struct flow *flow, struct ovs_key_nd *nd)
+{
+ memcpy(nd->nd_target, &flow->nd_target, sizeof flow->nd_target);
+ /* nd_sll and nd_tll are stored in arp_sha and arp_tha, respectively */
+ memcpy(nd->nd_sll, flow->arp_sha, ETH_ADDR_LEN);
+ memcpy(nd->nd_tll, flow->arp_tha, ETH_ADDR_LEN);
+}
+
+static void
+put_nd_key(const struct ovs_key_nd *nd, struct flow *flow)
+{
+ memcpy(&flow->nd_target, &flow->nd_target, sizeof flow->nd_target);
+ /* nd_sll and nd_tll are stored in arp_sha and arp_tha, respectively */
+ memcpy(flow->arp_sha, nd->nd_sll, ETH_ADDR_LEN);
+ memcpy(flow->arp_tha, nd->nd_tll, ETH_ADDR_LEN);
+}
+
+static enum slow_path_reason
+commit_set_nd_action(const struct flow *flow, struct flow *base_flow,
+ struct ofpbuf *odp_actions,
+ struct flow_wildcards *wc, bool use_masked)
+{
+ struct ovs_key_nd key, mask, base;
+
+ get_nd_key(flow, &key);
+ get_nd_key(base_flow, &base);
+ get_nd_key(&wc->masks, &mask);
+
+ if (commit(OVS_KEY_ATTR_ND, use_masked, &key, &base, &mask, sizeof key,
+ odp_actions)) {
+ put_nd_key(&base, base_flow);
+ put_nd_key(&mask, &wc->masks);
+ return SLOW_ACTION;
+ }
+
+ return 0;
+}
+
static enum slow_path_reason
commit_set_nw_action(const struct flow *flow, struct flow *base,
struct ofpbuf *odp_actions, struct flow_wildcards *wc,
case ETH_TYPE_IPV6:
commit_set_ipv6_action(flow, base, odp_actions, wc, use_masked);
- break;
+ return commit_set_nd_action(flow, base, odp_actions, wc, use_masked);
case ETH_TYPE_ARP:
return commit_set_arp_action(flow, base, odp_actions, wc);
commit_set_ether_addr_action(flow, base, odp_actions, wc, use_masked);
slow = commit_set_nw_action(flow, base, odp_actions, wc, use_masked);
commit_set_port_action(flow, base, odp_actions, wc, use_masked);
- commit_mpls_action(flow, base, odp_actions, wc);
+ commit_mpls_action(flow, base, odp_actions);
commit_vlan_action(flow->vlan_tci, base, odp_actions, wc);
commit_set_priority_action(flow, base, odp_actions, wc, use_masked);
commit_set_pkt_mark_action(flow, base, odp_actions, wc, use_masked);