return 0;
}
-static int push_vlan(struct sk_buff *skb, __be16 new_tci)
+static int push_vlan(struct sk_buff *skb, const struct ovs_key_8021q *q_key)
{
if (unlikely(vlan_tx_tag_present(skb))) {
u16 current_tag;
+ ETH_HLEN, VLAN_HLEN, 0));
}
- __vlan_hwaccel_put_tag(skb, ntohs(new_tci));
+ __vlan_hwaccel_put_tag(skb, ntohs(q_key->q_tci));
return 0;
}
-static bool is_ip(struct sk_buff *skb)
+static int set_eth_addr(struct sk_buff *skb,
+ const struct ovs_key_ethernet *eth_key)
{
- return (OVS_CB(skb)->flow->key.eth.type == htons(ETH_P_IP) &&
- skb->transport_header > skb->network_header);
+ int err;
+ err = make_writable(skb, ETH_HLEN);
+ if (unlikely(err))
+ return err;
+
+ memcpy(eth_hdr(skb)->h_source, eth_key->eth_src, ETH_HLEN);
+ memcpy(eth_hdr(skb)->h_dest, eth_key->eth_dst, ETH_HLEN);
+
+ return 0;
}
-static __sum16 *get_l4_checksum(struct sk_buff *skb)
+static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh,
+ __be32 *addr, __be32 new_addr)
{
- u8 nw_proto = OVS_CB(skb)->flow->key.ip.proto;
int transport_len = skb->len - skb_transport_offset(skb);
- if (nw_proto == IPPROTO_TCP) {
+
+ if (nh->protocol == IPPROTO_TCP) {
if (likely(transport_len >= sizeof(struct tcphdr)))
- return &tcp_hdr(skb)->check;
- } else if (nw_proto == IPPROTO_UDP) {
+ inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb,
+ *addr, new_addr, 1);
+ } else if (nh->protocol == IPPROTO_UDP) {
if (likely(transport_len >= sizeof(struct udphdr)))
- return &udp_hdr(skb)->check;
+ inet_proto_csum_replace4(&udp_hdr(skb)->check, skb,
+ *addr, new_addr, 1);
}
- return NULL;
+
+ csum_replace4(&nh->check, *addr, new_addr);
+ skb_clear_rxhash(skb);
+ *addr = new_addr;
}
-static int set_nw_addr(struct sk_buff *skb, const struct nlattr *a)
+static void set_ip_tos(struct sk_buff *skb, struct iphdr *nh, u8 new_tos)
+{
+ u8 old, new;
+
+ /* Set the DSCP bits and preserve the ECN bits. */
+ old = nh->tos;
+ new = new_tos | (nh->tos & INET_ECN_MASK);
+ csum_replace4(&nh->check, (__force __be32)old,
+ (__force __be32)new);
+ nh->tos = new;
+}
+
+static int set_ipv4(struct sk_buff *skb, const struct ovs_key_ipv4 *ipv4_key)
{
- __be32 new_nwaddr = nla_get_be32(a);
struct iphdr *nh;
- __sum16 *check;
- __be32 *nwaddr;
int err;
- if (unlikely(!is_ip(skb)))
- return 0;
-
err = make_writable(skb, skb_network_offset(skb) +
sizeof(struct iphdr));
if (unlikely(err))
return err;
nh = ip_hdr(skb);
- nwaddr = nla_type(a) == OVS_ACTION_ATTR_SET_NW_SRC ? &nh->saddr : &nh->daddr;
- check = get_l4_checksum(skb);
- if (likely(check))
- inet_proto_csum_replace4(check, skb, *nwaddr, new_nwaddr, 1);
- csum_replace4(&nh->check, *nwaddr, new_nwaddr);
+ if (ipv4_key->ipv4_src != nh->saddr)
+ set_ip_addr(skb, nh, &nh->saddr, ipv4_key->ipv4_src);
- skb_clear_rxhash(skb);
+ if (ipv4_key->ipv4_dst != nh->daddr)
+ set_ip_addr(skb, nh, &nh->daddr, ipv4_key->ipv4_dst);
- *nwaddr = new_nwaddr;
+ if (ipv4_key->ipv4_tos != nh->tos)
+ set_ip_tos(skb, nh, ipv4_key->ipv4_tos);
return 0;
}
-static int set_nw_tos(struct sk_buff *skb, u8 nw_tos)
+/* Must follow make_writable() since that can move the skb data. */
+static void set_tp_port(struct sk_buff *skb, __be16 *port,
+ __be16 new_port, __sum16 *check)
{
- struct iphdr *nh = ip_hdr(skb);
- u8 old, new;
- int err;
+ inet_proto_csum_replace2(check, skb, *port, new_port, 0);
+ *port = new_port;
+ skb_clear_rxhash(skb);
+}
- if (unlikely(!is_ip(skb)))
- return 0;
+static int set_udp_port(struct sk_buff *skb,
+ const struct ovs_key_udp *udp_port_key)
+{
+ struct udphdr *uh;
+ int err;
- err = make_writable(skb, skb_network_offset(skb) +
- sizeof(struct iphdr));
+ err = make_writable(skb, skb_transport_offset(skb) +
+ sizeof(struct udphdr));
if (unlikely(err))
return err;
- /* Set the DSCP bits and preserve the ECN bits. */
- old = nh->tos;
- new = nw_tos | (nh->tos & INET_ECN_MASK);
- csum_replace4(&nh->check, (__force __be32)old,
- (__force __be32)new);
- nh->tos = new;
+ uh = udp_hdr(skb);
+ if (udp_port_key->udp_src != uh->source)
+ set_tp_port(skb, &uh->source, udp_port_key->udp_src, &uh->check);
+
+ if (udp_port_key->udp_dst != uh->dest)
+ set_tp_port(skb, &uh->dest, udp_port_key->udp_dst, &uh->check);
return 0;
}
-static int set_tp_port(struct sk_buff *skb, const struct nlattr *a)
+static int set_tcp_port(struct sk_buff *skb,
+ const struct ovs_key_tcp *tcp_port_key)
{
- struct udphdr *th;
- __sum16 *check;
- __be16 *port;
+ struct tcphdr *th;
int err;
- if (unlikely(!is_ip(skb)))
- return 0;
-
err = make_writable(skb, skb_transport_offset(skb) +
sizeof(struct tcphdr));
if (unlikely(err))
return err;
- /* Must follow make_writable() since that can move the skb data. */
- check = get_l4_checksum(skb);
- if (unlikely(!check))
- return 0;
+ th = tcp_hdr(skb);
+ if (tcp_port_key->tcp_src != th->source)
+ set_tp_port(skb, &th->source, tcp_port_key->tcp_src, &th->check);
- /*
- * Update port and checksum.
- *
- * This is OK because source and destination port numbers are at the
- * same offsets in both UDP and TCP headers, and get_l4_checksum() only
- * supports those protocols.
- */
- th = udp_hdr(skb);
- port = nla_type(a) == OVS_ACTION_ATTR_SET_TP_SRC ? &th->source : &th->dest;
- inet_proto_csum_replace2(check, skb, *port, nla_get_be16(a), 0);
- *port = nla_get_be16(a);
- skb_clear_rxhash(skb);
+ if (tcp_port_key->tcp_dst != th->dest)
+ set_tp_port(skb, &th->dest, tcp_port_key->tcp_dst, &th->check);
return 0;
}
nla_len(acts_list), true);
}
+static int execute_set_action(struct sk_buff *skb,
+ const struct nlattr *nested_attr)
+{
+ int err;
+
+ switch (nla_type(nested_attr)) {
+ case OVS_KEY_ATTR_TUN_ID:
+ OVS_CB(skb)->tun_id = nla_get_be64(nested_attr);
+ err = 0;
+ break;
+
+ case OVS_KEY_ATTR_ETHERNET:
+ err = set_eth_addr(skb, nla_data(nested_attr));
+ break;
+
+ case OVS_KEY_ATTR_IPV4:
+ err = set_ipv4(skb, nla_data(nested_attr));
+ break;
+
+ case OVS_KEY_ATTR_TCP:
+ err = set_tcp_port(skb, nla_data(nested_attr));
+ break;
+
+ case OVS_KEY_ATTR_UDP:
+ err = set_udp_port(skb, nla_data(nested_attr));
+ break;
+ }
+ return err;
+}
+
/* Execute a list of actions against 'skb'. */
static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
const struct nlattr *attr, int len, bool keep_skb)
output_userspace(dp, skb, a);
break;
- case OVS_ACTION_ATTR_SET_TUNNEL:
- OVS_CB(skb)->tun_id = nla_get_be64(a);
- break;
-
- case OVS_ACTION_ATTR_PUSH_VLAN:
- err = push_vlan(skb, nla_get_be16(a));
- if (unlikely(err)) /* skb already freed */
+ case OVS_ACTION_ATTR_PUSH:
+ /* Only supported push action is on vlan tag. */
+ err = push_vlan(skb, nla_data(nla_data(a)));
+ if (unlikely(err)) /* skb already freed. */
return err;
break;
- case OVS_ACTION_ATTR_POP_VLAN:
+ case OVS_ACTION_ATTR_POP:
+ /* Only supported pop action is on vlan tag. */
err = pop_vlan(skb);
break;
- case OVS_ACTION_ATTR_SET_DL_SRC:
- err = make_writable(skb, ETH_HLEN);
- if (likely(!err))
- memcpy(eth_hdr(skb)->h_source, nla_data(a), ETH_ALEN);
- break;
-
- case OVS_ACTION_ATTR_SET_DL_DST:
- err = make_writable(skb, ETH_HLEN);
- if (likely(!err))
- memcpy(eth_hdr(skb)->h_dest, nla_data(a), ETH_ALEN);
- break;
-
- case OVS_ACTION_ATTR_SET_NW_SRC:
- case OVS_ACTION_ATTR_SET_NW_DST:
- err = set_nw_addr(skb, a);
- break;
-
- case OVS_ACTION_ATTR_SET_NW_TOS:
- err = set_nw_tos(skb, nla_get_u8(a));
- break;
-
- case OVS_ACTION_ATTR_SET_TP_SRC:
- case OVS_ACTION_ATTR_SET_TP_DST:
- err = set_tp_port(skb, a);
+ case OVS_ACTION_ATTR_SET:
+ err = execute_set_action(skb, nla_data(a));
break;
case OVS_ACTION_ATTR_SET_PRIORITY:
return 0;
}
-static int validate_actions(const struct nlattr *attr, int depth);
+static int validate_actions(const struct nlattr *attr,
+ const struct sw_flow_key *key, int depth);
-static int validate_sample(const struct nlattr *attr, int depth)
+static int validate_sample(const struct nlattr *attr,
+ const struct sw_flow_key *key, int depth)
{
static const struct nla_policy sample_policy[OVS_SAMPLE_ATTR_MAX + 1] =
{
if (!a[OVS_SAMPLE_ATTR_ACTIONS])
return -EINVAL;
- return validate_actions(a[OVS_SAMPLE_ATTR_ACTIONS], (depth + 1));
+ return validate_actions(a[OVS_SAMPLE_ATTR_ACTIONS], key, (depth + 1));
+}
+
+static int validate_action_key(const struct nlattr *a,
+ const struct sw_flow_key *flow_key)
+{
+ int act_type = nla_type(a);
+ const struct nlattr *ovs_key = nla_data(a);
+ int key_type = nla_type(ovs_key);
+
+ /* There can be only one key in a action */
+ if (nla_total_size(nla_len(ovs_key)) != nla_len(a))
+ return -EINVAL;
+
+ if (key_type > OVS_KEY_ATTR_MAX ||
+ nla_len(ovs_key) != ovs_key_lens[key_type])
+ return -EINVAL;
+
+#define ACTION(act, key) ((act << 8) | key)
+
+ switch(ACTION(act_type, key_type)) {
+ const struct ovs_key_ipv4 *ipv4_key;
+ const struct ovs_key_8021q *q_key;
+
+ case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TUN_ID):
+ case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_ETHERNET):
+ break;
+
+ case ACTION(OVS_ACTION_ATTR_PUSH, OVS_KEY_ATTR_8021Q):
+ q_key = nla_data(ovs_key);
+ if (q_key->q_tpid != htons(ETH_P_8021Q))
+ return -EINVAL;
+
+ if (q_key->q_tci & htons(VLAN_TAG_PRESENT))
+ return -EINVAL;
+ break;
+
+ case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_IPV4):
+ if (flow_key->eth.type != htons(ETH_P_IP))
+ return -EINVAL;
+
+ if (!flow_key->ipv4.addr.src || !flow_key->ipv4.addr.dst)
+ return -EINVAL;
+
+ ipv4_key = nla_data(ovs_key);
+ if (ipv4_key->ipv4_proto != flow_key->ip.proto)
+ return -EINVAL;
+
+ if (ipv4_key->ipv4_tos & INET_ECN_MASK)
+ return -EINVAL;
+ break;
+
+ case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TCP):
+ if (flow_key->ip.proto != IPPROTO_TCP)
+ return -EINVAL;
+
+ if (!flow_key->ipv4.tp.src || !flow_key->ipv4.tp.dst)
+ return -EINVAL;
+
+ break;
+
+ case ACTION(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_UDP):
+ if (flow_key->ip.proto != IPPROTO_UDP)
+ return -EINVAL;
+
+ if (!flow_key->ipv4.tp.src || !flow_key->ipv4.tp.dst)
+ return -EINVAL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+#undef ACTION
+ return 0;
}
static int validate_userspace(const struct nlattr *attr)
return 0;
}
-static int validate_actions(const struct nlattr *attr, int depth)
+static int validate_actions(const struct nlattr *attr,
+ const struct sw_flow_key *key, int depth)
{
const struct nlattr *a;
int rem, err;
static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = {
[OVS_ACTION_ATTR_OUTPUT] = 4,
[OVS_ACTION_ATTR_USERSPACE] = (u32)-1,
- [OVS_ACTION_ATTR_PUSH_VLAN] = 2,
- [OVS_ACTION_ATTR_POP_VLAN] = 0,
- [OVS_ACTION_ATTR_SET_DL_SRC] = ETH_ALEN,
- [OVS_ACTION_ATTR_SET_DL_DST] = ETH_ALEN,
- [OVS_ACTION_ATTR_SET_NW_SRC] = 4,
- [OVS_ACTION_ATTR_SET_NW_DST] = 4,
- [OVS_ACTION_ATTR_SET_NW_TOS] = 1,
- [OVS_ACTION_ATTR_SET_TP_SRC] = 2,
- [OVS_ACTION_ATTR_SET_TP_DST] = 2,
- [OVS_ACTION_ATTR_SET_TUNNEL] = 8,
+ [OVS_ACTION_ATTR_PUSH] = (u32)-1,
+ [OVS_ACTION_ATTR_POP] = 2,
+ [OVS_ACTION_ATTR_SET] = (u32)-1,
[OVS_ACTION_ATTR_SET_PRIORITY] = 4,
[OVS_ACTION_ATTR_POP_PRIORITY] = 0,
[OVS_ACTION_ATTR_SAMPLE] = (u32)-1
case OVS_ACTION_ATTR_UNSPEC:
return -EINVAL;
- case OVS_ACTION_ATTR_POP_VLAN:
- case OVS_ACTION_ATTR_SET_DL_SRC:
- case OVS_ACTION_ATTR_SET_DL_DST:
- case OVS_ACTION_ATTR_SET_NW_SRC:
- case OVS_ACTION_ATTR_SET_NW_DST:
- case OVS_ACTION_ATTR_SET_TP_SRC:
- case OVS_ACTION_ATTR_SET_TP_DST:
- case OVS_ACTION_ATTR_SET_TUNNEL:
case OVS_ACTION_ATTR_SET_PRIORITY:
case OVS_ACTION_ATTR_POP_PRIORITY:
/* No validation needed. */
return -EINVAL;
break;
- case OVS_ACTION_ATTR_PUSH_VLAN:
- if (nla_get_be16(a) & htons(VLAN_CFI_MASK))
+
+ case OVS_ACTION_ATTR_POP:
+ if (nla_get_u16(a) != OVS_KEY_ATTR_8021Q)
return -EINVAL;
break;
- case OVS_ACTION_ATTR_SET_NW_TOS:
- if (nla_get_u8(a) & INET_ECN_MASK)
- return -EINVAL;
+ case OVS_ACTION_ATTR_SET:
+ case OVS_ACTION_ATTR_PUSH:
+ err = validate_action_key(a, key);
+ if (err)
+ return err;
break;
case OVS_ACTION_ATTR_SAMPLE:
- err = validate_sample(a, depth);
+ err = validate_sample(a, key, depth);
if (err)
return err;
break;
default:
- return -EOPNOTSUPP;
+ return -EINVAL;
}
}
return 0;
}
+
static void clear_stats(struct sw_flow *flow)
{
flow->used = 0;
nla_len(a[OVS_PACKET_ATTR_PACKET]) < ETH_HLEN)
goto err;
- err = validate_actions(a[OVS_PACKET_ATTR_ACTIONS], 0);
- if (err)
- goto err;
-
len = nla_len(a[OVS_PACKET_ATTR_PACKET]);
packet = __dev_alloc_skb(NET_IP_ALIGN + len, GFP_KERNEL);
err = -ENOMEM;
if (err)
goto err_flow_put;
+ err = validate_actions(a[OVS_PACKET_ATTR_ACTIONS], &flow->key, 0);
+ if (err)
+ goto err_flow_put;
+
flow->hash = flow_hash(&flow->key, key_len);
acts = flow_actions_alloc(a[OVS_PACKET_ATTR_ACTIONS]);
/* Validate actions. */
if (a[OVS_FLOW_ATTR_ACTIONS]) {
- error = validate_actions(a[OVS_FLOW_ATTR_ACTIONS], 0);
+ error = validate_actions(a[OVS_FLOW_ATTR_ACTIONS], &key, 0);
if (error)
goto error;
} else if (info->genlhdr->cmd == OVS_FLOW_CMD_NEW) {
}
/* The size of the argument for each %OVS_KEY_ATTR_* Netlink attribute. */
-static const u32 key_lens[OVS_KEY_ATTR_MAX + 1] = {
+const u32 ovs_key_lens[OVS_KEY_ATTR_MAX + 1] = {
[OVS_KEY_ATTR_TUN_ID] = 8,
[OVS_KEY_ATTR_IN_PORT] = 4,
[OVS_KEY_ATTR_ETHERNET] = sizeof(struct ovs_key_ethernet),
int type = nla_type(nla);
- if (type > OVS_KEY_ATTR_MAX || nla_len(nla) != key_lens[type])
- goto invalid;
+ if (type > OVS_KEY_ATTR_MAX || nla_len(nla) != ovs_key_lens[type])
+ goto invalid;
#define TRANSITION(PREV_TYPE, TYPE) (((PREV_TYPE) << 16) | (TYPE))
switch (TRANSITION(prev_type, type)) {
nla_for_each_nested(nla, attr, rem) {
int type = nla_type(nla);
- if (type > OVS_KEY_ATTR_MAX || nla_len(nla) != key_lens[type])
- return -EINVAL;
+ if (type > OVS_KEY_ATTR_MAX || nla_len(nla) != ovs_key_lens[type])
+ return -EINVAL;
switch (TRANSITION(prev_type, type)) {
case TRANSITION(OVS_KEY_ATTR_UNSPEC, OVS_KEY_ATTR_TUN_ID):
u32 flow_hash(const struct sw_flow_key *key, int key_len);
struct sw_flow *flow_tbl_next(struct flow_table *table, u32 *bucket, u32 *idx);
+extern const u32 ovs_key_lens[OVS_KEY_ATTR_MAX + 1];
#endif /* flow.h */
#define OVS_USERSPACE_ATTR_MAX (__OVS_USERSPACE_ATTR_MAX - 1)
-/* Action types. */
+/**
+ * enum ovs_action_attr - Action types.
+ *
+ * @OVS_ACTION_ATTR_OUTPUT: Output packet to port passed as NLA data.
+ * @OVS_ACTION_ATTR_USERSPACE: Nested %OVS_USERSPACE_ATTR_ attributes specifying
+ * PID.
+ * @OVS_ACTION_ATTR_PUSH: Nested %OVS_KEY_ATTR_* attribute specifying header to
+ * push to given packet.
+ * E.g. Push vlan tag action would be NLA of %OVS_ACTION_ATTR_PUSH type with
+ * nested attribute of type %OVS_KEY_ATTR_8021Q with struct ovs_key_8021q
+ * as NLA data.
+ * @OVS_ACTION_ATTR_POP: Pop header according to %OVS_KEY_ATTR_ sent as
+ * attribute data.
+ * @OVS_ACTION_ATTR_SET: Nested %OVS_KEY_ATTR_* attribute specifying the
+ * field to set to given packet.
+ * @OVS_ACTION_ATTR_SET_PRIORITY: A set skb->priority to 32-bit number passed
+ * as NLA data.
+ * @OVS_ACTION_ATTR_POP_PRIORITY: Restore skb->priority to original value.
+ * @OVS_ACTION_ATTR_SAMPLE: Execute set of actions according to probability
+ * %OVS_SAMPLE_ATTR_PROBABILITY.
+ *
+ * Only a single field can be set with a single %OVS_ACTION_ATTR_{SET,PUSH}.
+ */
+
enum ovs_action_attr {
OVS_ACTION_ATTR_UNSPEC,
OVS_ACTION_ATTR_OUTPUT, /* Output to switch port. */
OVS_ACTION_ATTR_USERSPACE, /* Nested OVS_USERSPACE_ATTR_*. */
- OVS_ACTION_ATTR_PUSH_VLAN, /* Set the 802.1q TCI value. */
- OVS_ACTION_ATTR_POP_VLAN, /* Strip the 802.1q header. */
- OVS_ACTION_ATTR_SET_DL_SRC, /* Ethernet source address. */
- OVS_ACTION_ATTR_SET_DL_DST, /* Ethernet destination address. */
- OVS_ACTION_ATTR_SET_NW_SRC, /* IPv4 source address. */
- OVS_ACTION_ATTR_SET_NW_DST, /* IPv4 destination address. */
- OVS_ACTION_ATTR_SET_NW_TOS, /* IP ToS/DSCP field (6 bits). */
- OVS_ACTION_ATTR_SET_TP_SRC, /* TCP/UDP source port. */
- OVS_ACTION_ATTR_SET_TP_DST, /* TCP/UDP destination port. */
- OVS_ACTION_ATTR_SET_TUNNEL, /* Set the encapsulating tunnel ID. */
+ OVS_ACTION_ATTR_PUSH, /* Push packet header. */
+ OVS_ACTION_ATTR_POP, /* Pop packet header. */
+ OVS_ACTION_ATTR_SET, /* Set packet field(s). */
OVS_ACTION_ATTR_SET_PRIORITY, /* Set skb->priority. */
OVS_ACTION_ATTR_POP_PRIORITY, /* Restore original skb->priority. */
OVS_ACTION_ATTR_SAMPLE, /* Nested OVS_SAMPLE_ATTR_*. */
static int dp_netdev_output_userspace(struct dp_netdev *, const struct ofpbuf *,
int queue_no, const struct flow *,
uint64_t arg);
-static int dp_netdev_execute_actions(struct dp_netdev *,
- struct ofpbuf *, struct flow *,
- const struct nlattr *actions,
- size_t actions_len);
+static void dp_netdev_execute_actions(struct dp_netdev *,
+ struct ofpbuf *, struct flow *,
+ const struct nlattr *actions,
+ size_t actions_len);
static struct dpif_class dpif_dummy_class;
flow_extract(©, 0, -1, &key);
error = dpif_netdev_flow_from_nlattrs(key_attrs, key_len, &key);
if (!error) {
- error = dp_netdev_execute_actions(dp, ©, &key,
- actions, actions_len);
+ dp_netdev_execute_actions(dp, ©, &key,
+ actions, actions_len);
}
ofpbuf_uninit(©);
}
static void
-dp_netdev_set_dl_src(struct ofpbuf *packet, const uint8_t dl_addr[ETH_ADDR_LEN])
+dp_netdev_set_dl(struct ofpbuf *packet, const struct ovs_key_ethernet *eth_key)
{
struct eth_header *eh = packet->l2;
- memcpy(eh->eth_src, dl_addr, sizeof eh->eth_src);
+
+ memcpy(eh->eth_src, eth_key->eth_src, sizeof eh->eth_src);
+ memcpy(eh->eth_dst, eth_key->eth_dst, sizeof eh->eth_dst);
}
static void
-dp_netdev_set_dl_dst(struct ofpbuf *packet, const uint8_t dl_addr[ETH_ADDR_LEN])
+dp_netdev_set_ip_addr(struct ofpbuf *packet, ovs_be32 *addr, ovs_be32 new_addr)
{
- struct eth_header *eh = packet->l2;
- memcpy(eh->eth_dst, dl_addr, sizeof eh->eth_dst);
+ struct ip_header *nh = packet->l3;
+
+ if (nh->ip_proto == IPPROTO_TCP && packet->l7) {
+ struct tcp_header *th = packet->l4;
+ th->tcp_csum = recalc_csum32(th->tcp_csum, *addr, new_addr);
+ } else if (nh->ip_proto == IPPROTO_UDP && packet->l7) {
+ struct udp_header *uh = packet->l4;
+ if (uh->udp_csum) {
+ uh->udp_csum = recalc_csum32(uh->udp_csum, *addr, new_addr);
+ if (!uh->udp_csum) {
+ uh->udp_csum = htons(0xffff);
+ }
+ }
+ }
+ nh->ip_csum = recalc_csum32(nh->ip_csum, *addr, new_addr);
+ *addr = new_addr;
}
-static bool
-is_ip(const struct ofpbuf *packet, const struct flow *key)
+static void
+dp_netdev_set_ip_tos(struct ip_header *nh, uint8_t new_tos)
{
- return key->dl_type == htons(ETH_TYPE_IP) && packet->l4;
+ uint8_t *field = &nh->ip_tos;
+
+ /* Set the DSCP bits and preserve the ECN bits. */
+ uint8_t new = new_tos | (nh->ip_tos & IP_ECN_MASK);
+
+ nh->ip_csum = recalc_csum16(nh->ip_csum, htons((uint16_t)*field),
+ htons((uint16_t) new));
+ *field = new;
}
static void
-dp_netdev_set_nw_addr(struct ofpbuf *packet, const struct flow *key,
- const struct nlattr *a)
-{
- if (is_ip(packet, key)) {
- struct ip_header *nh = packet->l3;
- ovs_be32 ip = nl_attr_get_be32(a);
- uint16_t type = nl_attr_type(a);
- ovs_be32 *field;
-
- field = type == OVS_ACTION_ATTR_SET_NW_SRC ? &nh->ip_src : &nh->ip_dst;
- if (key->nw_proto == IPPROTO_TCP && packet->l7) {
- struct tcp_header *th = packet->l4;
- th->tcp_csum = recalc_csum32(th->tcp_csum, *field, ip);
- } else if (key->nw_proto == IPPROTO_UDP && packet->l7) {
- struct udp_header *uh = packet->l4;
- if (uh->udp_csum) {
- uh->udp_csum = recalc_csum32(uh->udp_csum, *field, ip);
- if (!uh->udp_csum) {
- uh->udp_csum = htons(0xffff);
- }
- }
- }
- nh->ip_csum = recalc_csum32(nh->ip_csum, *field, ip);
- *field = ip;
+dp_netdev_set_ipv4(struct ofpbuf *packet, const struct ovs_key_ipv4 *ipv4_key)
+{
+ struct ip_header *nh = packet->l3;
+
+ if (nh->ip_src != ipv4_key->ipv4_src) {
+ dp_netdev_set_ip_addr(packet, &nh->ip_src, ipv4_key->ipv4_src);
+ }
+ if (nh->ip_dst != ipv4_key->ipv4_dst) {
+ dp_netdev_set_ip_addr(packet, &nh->ip_dst, ipv4_key->ipv4_dst);
+ }
+ if (nh->ip_tos != ipv4_key->ipv4_tos) {
+ dp_netdev_set_ip_tos(nh, ipv4_key->ipv4_tos);
}
}
static void
-dp_netdev_set_nw_tos(struct ofpbuf *packet, const struct flow *key,
- uint8_t nw_tos)
+dp_netdev_set_port(ovs_be16 *port, ovs_be16 new_port, ovs_be16 *csum)
{
- if (is_ip(packet, key)) {
- struct ip_header *nh = packet->l3;
- uint8_t *field = &nh->ip_tos;
+ *csum = recalc_csum16(*csum, *port, new_port);
+ *port = new_port;
+}
- /* Set the DSCP bits and preserve the ECN bits. */
- uint8_t new = nw_tos | (nh->ip_tos & IP_ECN_MASK);
+static void
+dp_netdev_set_tcp_port(struct ofpbuf *packet, const struct ovs_key_tcp *tcp_key)
+{
+ struct tcp_header *th = packet->l4;
- nh->ip_csum = recalc_csum16(nh->ip_csum, htons((uint16_t)*field),
- htons((uint16_t) new));
- *field = new;
+ if (th->tcp_src != tcp_key->tcp_src) {
+ dp_netdev_set_port(&th->tcp_src, tcp_key->tcp_src, &th->tcp_csum);
+ }
+ if (th->tcp_dst != tcp_key->tcp_dst) {
+ dp_netdev_set_port(&th->tcp_dst, tcp_key->tcp_dst, &th->tcp_csum);
}
}
static void
-dp_netdev_set_tp_port(struct ofpbuf *packet, const struct flow *key,
- const struct nlattr *a)
-{
- if (is_ip(packet, key)) {
- uint16_t type = nl_attr_type(a);
- ovs_be16 port = nl_attr_get_be16(a);
- ovs_be16 *field;
-
- if (key->nw_proto == IPPROTO_TCP && packet->l7) {
- struct tcp_header *th = packet->l4;
- field = (type == OVS_ACTION_ATTR_SET_TP_SRC
- ? &th->tcp_src : &th->tcp_dst);
- th->tcp_csum = recalc_csum16(th->tcp_csum, *field, port);
- *field = port;
- } else if (key->nw_proto == IPPROTO_UDP && packet->l7) {
- struct udp_header *uh = packet->l4;
- field = (type == OVS_ACTION_ATTR_SET_TP_SRC
- ? &uh->udp_src : &uh->udp_dst);
- uh->udp_csum = recalc_csum16(uh->udp_csum, *field, port);
- *field = port;
- } else {
- return;
- }
+dp_netdev_set_udp_port(struct ofpbuf *packet, const struct ovs_key_udp *udp_key)
+{
+ struct udp_header *uh = packet->l4;
+
+ if (uh->udp_src != udp_key->udp_src) {
+ dp_netdev_set_port(&uh->udp_src, udp_key->udp_src, &uh->udp_csum);
+ }
+ if (uh->udp_dst != udp_key->udp_dst) {
+ dp_netdev_set_port(&uh->udp_dst, udp_key->udp_dst, &uh->udp_csum);
}
}
dp_netdev_output_userspace(dp, packet, DPIF_UC_ACTION, key, userdata);
}
-static int
+static void
+execute_set_action(struct ofpbuf *packet, const struct nlattr *a)
+{
+ enum ovs_key_attr type = nl_attr_type(a);
+ switch (type) {
+ case OVS_KEY_ATTR_TUN_ID:
+ break;
+
+ case OVS_KEY_ATTR_ETHERNET:
+ dp_netdev_set_dl(packet,
+ nl_attr_get_unspec(a, sizeof(struct ovs_key_ethernet)));
+ break;
+
+ case OVS_KEY_ATTR_IPV4:
+ dp_netdev_set_ipv4(packet,
+ nl_attr_get_unspec(a, sizeof(struct ovs_key_ipv4)));
+ break;
+
+ case OVS_KEY_ATTR_TCP:
+ dp_netdev_set_tcp_port(packet,
+ nl_attr_get_unspec(a, sizeof(struct ovs_key_tcp)));
+ break;
+
+ case OVS_KEY_ATTR_UDP:
+ dp_netdev_set_udp_port(packet,
+ nl_attr_get_unspec(a, sizeof(struct ovs_key_udp)));
+ break;
+
+ case OVS_KEY_ATTR_UNSPEC:
+ case OVS_KEY_ATTR_ETHERTYPE:
+ case OVS_KEY_ATTR_IPV6:
+ case OVS_KEY_ATTR_IN_PORT:
+ case OVS_KEY_ATTR_8021Q:
+ case OVS_KEY_ATTR_ICMP:
+ case OVS_KEY_ATTR_ICMPV6:
+ case OVS_KEY_ATTR_ARP:
+ case OVS_KEY_ATTR_ND:
+ case __OVS_KEY_ATTR_MAX:
+ default:
+ NOT_REACHED();
+ }
+}
+
+static void
dp_netdev_execute_actions(struct dp_netdev *dp,
struct ofpbuf *packet, struct flow *key,
const struct nlattr *actions,
unsigned int left;
NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
+ const struct nlattr *nested;
+ const struct ovs_key_8021q *q_key;
int type = nl_attr_type(a);
switch ((enum ovs_action_attr) type) {
dp_netdev_action_userspace(dp, packet, key, a);
break;
- case OVS_ACTION_ATTR_PUSH_VLAN:
- eth_push_vlan(packet, nl_attr_get_be16(a));
+ case OVS_ACTION_ATTR_PUSH:
+ nested = nl_attr_get(a);
+ assert(nl_attr_type(nested) == OVS_KEY_ATTR_8021Q);
+ q_key = nl_attr_get_unspec(nested, sizeof(*q_key));
+ eth_push_vlan(packet, q_key->q_tci);
break;
- case OVS_ACTION_ATTR_POP_VLAN:
+ case OVS_ACTION_ATTR_POP:
+ assert(nl_attr_get_u16(a) == OVS_KEY_ATTR_8021Q);
dp_netdev_pop_vlan(packet);
break;
- case OVS_ACTION_ATTR_SET_DL_SRC:
- dp_netdev_set_dl_src(packet, nl_attr_get_unspec(a, ETH_ADDR_LEN));
- break;
-
- case OVS_ACTION_ATTR_SET_DL_DST:
- dp_netdev_set_dl_dst(packet, nl_attr_get_unspec(a, ETH_ADDR_LEN));
- break;
-
- case OVS_ACTION_ATTR_SET_NW_SRC:
- case OVS_ACTION_ATTR_SET_NW_DST:
- dp_netdev_set_nw_addr(packet, key, a);
- break;
-
- case OVS_ACTION_ATTR_SET_NW_TOS:
- dp_netdev_set_nw_tos(packet, key, nl_attr_get_u8(a));
- break;
-
- case OVS_ACTION_ATTR_SET_TP_SRC:
- case OVS_ACTION_ATTR_SET_TP_DST:
- dp_netdev_set_tp_port(packet, key, a);
+ case OVS_ACTION_ATTR_SET:
+ execute_set_action(packet, nl_attr_get(a));
break;
case OVS_ACTION_ATTR_SAMPLE:
dp_netdev_sample(dp, packet, key, a);
break;
- case OVS_ACTION_ATTR_SET_TUNNEL:
case OVS_ACTION_ATTR_SET_PRIORITY:
case OVS_ACTION_ATTR_POP_PRIORITY:
/* not implemented */
NOT_REACHED();
}
}
- return 0;
}
const struct dpif_class dpif_netdev_class = {
#include "timeval.h"
#include "util.h"
+static void
+format_odp_key_attr(const struct nlattr *a, struct ds *ds);
+
/* The interface between userspace and kernel uses an "OVS_*" prefix.
* Since this is fairly non-specific for the OVS userspace components,
* "ODP_*" (Open vSwitch Datapath) is used as the prefix for
* - For an action with a variable-length argument, returns -2.
*
* - For an invalid 'type', returns -1. */
-int
+static int
odp_action_len(uint16_t type)
{
if (type > OVS_ACTION_ATTR_MAX) {
switch ((enum ovs_action_attr) type) {
case OVS_ACTION_ATTR_OUTPUT: return 4;
case OVS_ACTION_ATTR_USERSPACE: return -2;
- case OVS_ACTION_ATTR_PUSH_VLAN: return 2;
- case OVS_ACTION_ATTR_POP_VLAN: return 0;
- case OVS_ACTION_ATTR_SET_DL_SRC: return ETH_ADDR_LEN;
- case OVS_ACTION_ATTR_SET_DL_DST: return ETH_ADDR_LEN;
- case OVS_ACTION_ATTR_SET_NW_SRC: return 4;
- case OVS_ACTION_ATTR_SET_NW_DST: return 4;
- case OVS_ACTION_ATTR_SET_NW_TOS: return 1;
- case OVS_ACTION_ATTR_SET_TP_SRC: return 2;
- case OVS_ACTION_ATTR_SET_TP_DST: return 2;
- case OVS_ACTION_ATTR_SET_TUNNEL: return 8;
+ case OVS_ACTION_ATTR_PUSH: return -2;
+ case OVS_ACTION_ATTR_POP: return 2;
+ case OVS_ACTION_ATTR_SET: return -2;
case OVS_ACTION_ATTR_SET_PRIORITY: return 4;
case OVS_ACTION_ATTR_POP_PRIORITY: return 0;
case OVS_ACTION_ATTR_SAMPLE: return -2;
}
-void
+static void
format_odp_action(struct ds *ds, const struct nlattr *a)
{
- const uint8_t *eth;
int expected_len;
- ovs_be32 ip;
+ enum ovs_action_attr type = nl_attr_type(a);
expected_len = odp_action_len(nl_attr_type(a));
if (expected_len != -2 && nl_attr_get_size(a) != expected_len) {
return;
}
- switch (nl_attr_type(a)) {
+ switch (type) {
+
case OVS_ACTION_ATTR_OUTPUT:
ds_put_format(ds, "%"PRIu16, nl_attr_get_u32(a));
break;
case OVS_ACTION_ATTR_USERSPACE:
format_odp_userspace_action(ds, a);
break;
- case OVS_ACTION_ATTR_SET_TUNNEL:
- ds_put_format(ds, "set_tunnel(%#"PRIx64")",
- ntohll(nl_attr_get_be64(a)));
- break;
- case OVS_ACTION_ATTR_PUSH_VLAN:
- ds_put_format(ds, "push_vlan(vid=%"PRIu16",pcp=%d)",
- vlan_tci_to_vid(nl_attr_get_be16(a)),
- vlan_tci_to_pcp(nl_attr_get_be16(a)));
- break;
- case OVS_ACTION_ATTR_POP_VLAN:
- ds_put_format(ds, "pop_vlan");
+ case OVS_ACTION_ATTR_SET:
+ ds_put_cstr(ds, "set(");
+ format_odp_key_attr(nl_attr_get(a), ds);
+ ds_put_cstr(ds, ")");
break;
- case OVS_ACTION_ATTR_SET_DL_SRC:
- eth = nl_attr_get_unspec(a, ETH_ADDR_LEN);
- ds_put_format(ds, "set_dl_src("ETH_ADDR_FMT")", ETH_ADDR_ARGS(eth));
+ case OVS_ACTION_ATTR_PUSH:
+ ds_put_cstr(ds, "push(");
+ format_odp_key_attr(nl_attr_get(a), ds);
+ ds_put_cstr(ds, ")");
break;
- case OVS_ACTION_ATTR_SET_DL_DST:
- eth = nl_attr_get_unspec(a, ETH_ADDR_LEN);
- ds_put_format(ds, "set_dl_dst("ETH_ADDR_FMT")", ETH_ADDR_ARGS(eth));
- break;
- case OVS_ACTION_ATTR_SET_NW_SRC:
- ip = nl_attr_get_be32(a);
- ds_put_format(ds, "set_nw_src("IP_FMT")", IP_ARGS(&ip));
- break;
- case OVS_ACTION_ATTR_SET_NW_DST:
- ip = nl_attr_get_be32(a);
- ds_put_format(ds, "set_nw_dst("IP_FMT")", IP_ARGS(&ip));
- break;
- case OVS_ACTION_ATTR_SET_NW_TOS:
- ds_put_format(ds, "set_nw_tos(%"PRIu8")", nl_attr_get_u8(a));
- break;
- case OVS_ACTION_ATTR_SET_TP_SRC:
- ds_put_format(ds, "set_tp_src(%"PRIu16")", ntohs(nl_attr_get_be16(a)));
- break;
- case OVS_ACTION_ATTR_SET_TP_DST:
- ds_put_format(ds, "set_tp_dst(%"PRIu16")", ntohs(nl_attr_get_be16(a)));
+ case OVS_ACTION_ATTR_POP:
+ if (nl_attr_get_u16(a) == OVS_KEY_ATTR_8021Q) {
+ ds_put_cstr(ds, "pop(vlan)");
+ } else {
+ ds_put_format(ds, "pop(key%"PRIu16")", nl_attr_get_u16(a));
+ }
break;
case OVS_ACTION_ATTR_SET_PRIORITY:
ds_put_format(ds, "set_priority(%#"PRIx32")", nl_attr_get_u32(a));
case OVS_ACTION_ATTR_SAMPLE:
format_odp_sample_action(ds, a);
break;
+ case OVS_ACTION_ATTR_UNSPEC:
+ case __OVS_ACTION_ATTR_MAX:
default:
format_generic_odp_action(ds, a);
break;
return odp_port;
}
}
-
-int odp_action_len(uint16_t type);
-void format_odp_action(struct ds *, const struct nlattr *);
void format_odp_actions(struct ds *, const struct nlattr *odp_actions,
size_t actions_len);
struct ofport_dpif *port;
switch (nl_attr_type(a)) {
+ const struct nlattr *nested;
case OVS_ACTION_ATTR_OUTPUT:
port = get_odp_port(ofproto, nl_attr_get_u32(a));
if (port && port->bundle && port->bundle->bond) {
}
break;
- case OVS_ACTION_ATTR_POP_VLAN:
- vlan_tci = htons(0);
+ case OVS_ACTION_ATTR_POP:
+ if (nl_attr_get_u16(a) == OVS_KEY_ATTR_8021Q) {
+ vlan_tci = htons(0);
+ }
break;
- case OVS_ACTION_ATTR_PUSH_VLAN:
- vlan_tci = nl_attr_get_be16(a);
+ case OVS_ACTION_ATTR_PUSH:
+ nested = nl_attr_get(a);
+ if (nl_attr_type(nested) == OVS_KEY_ATTR_8021Q) {
+ const struct ovs_key_8021q *q_key;
+
+ q_key = nl_attr_get_unspec(nested, sizeof(*q_key));
+ vlan_tci = q_key->q_tci;
+ }
break;
}
}
}
static void
-commit_vlan_tci(struct action_xlate_ctx *ctx, ovs_be16 vlan_tci)
+commit_action__(struct ofpbuf *odp_actions,
+ enum ovs_action_attr act_type,
+ enum ovs_key_attr key_type,
+ const void *key, size_t key_size)
{
- struct flow *base = &ctx->base_flow;
- struct ofpbuf *odp_actions = ctx->odp_actions;
+ size_t offset = nl_msg_start_nested(odp_actions, act_type);
- if (base->vlan_tci != vlan_tci) {
- if (!(vlan_tci & htons(VLAN_CFI))) {
- nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
- } else {
- if (base->vlan_tci != htons(0)) {
- nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_VLAN);
- }
- nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_PUSH_VLAN,
- vlan_tci & ~htons(VLAN_CFI));
- }
- base->vlan_tci = vlan_tci;
+ nl_msg_put_unspec(odp_actions, key_type, key, key_size);
+ nl_msg_end_nested(odp_actions, offset);
+}
+
+static void
+commit_set_tun_id_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions)
+{
+ if (base->tun_id == flow->tun_id) {
+ return;
}
+ base->tun_id = flow->tun_id;
+
+ commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
+ OVS_KEY_ATTR_TUN_ID, &base->tun_id, sizeof(base->tun_id));
}
static void
-commit_odp_actions(struct action_xlate_ctx *ctx)
+commit_set_ether_addr_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions)
+{
+ struct ovs_key_ethernet eth_key;
+
+ if (eth_addr_equals(base->dl_src, flow->dl_src) &&
+ eth_addr_equals(base->dl_dst, flow->dl_dst)) {
+ return;
+ }
+
+ memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN);
+ memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN);
+
+ memcpy(eth_key.eth_src, base->dl_src, ETH_ADDR_LEN);
+ memcpy(eth_key.eth_dst, base->dl_dst, ETH_ADDR_LEN);
+
+ commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
+ OVS_KEY_ATTR_ETHERNET, ð_key, sizeof(eth_key));
+}
+
+static void
+commit_vlan_action(struct action_xlate_ctx *ctx, ovs_be16 new_tci)
{
- const struct flow *flow = &ctx->flow;
struct flow *base = &ctx->base_flow;
- struct ofpbuf *odp_actions = ctx->odp_actions;
- if (base->tun_id != flow->tun_id) {
- nl_msg_put_be64(odp_actions, OVS_ACTION_ATTR_SET_TUNNEL, flow->tun_id);
- base->tun_id = flow->tun_id;
+ if (base->vlan_tci == new_tci) {
+ return;
}
- if (base->nw_src != flow->nw_src) {
- nl_msg_put_be32(odp_actions, OVS_ACTION_ATTR_SET_NW_SRC, flow->nw_src);
- base->nw_src = flow->nw_src;
+ if (base->vlan_tci & htons(VLAN_CFI)) {
+ nl_msg_put_u16(ctx->odp_actions, OVS_ACTION_ATTR_POP,
+ OVS_KEY_ATTR_8021Q);
}
- if (base->nw_dst != flow->nw_dst) {
- nl_msg_put_be32(odp_actions, OVS_ACTION_ATTR_SET_NW_DST, flow->nw_dst);
- base->nw_dst = flow->nw_dst;
+ if (new_tci & htons(VLAN_CFI)) {
+ struct ovs_key_8021q q_key;
+
+ q_key.q_tpid = htons(ETH_TYPE_VLAN);
+ q_key.q_tci = new_tci & ~htons(VLAN_CFI);
+
+ commit_action__(ctx->odp_actions, OVS_ACTION_ATTR_PUSH,
+ OVS_KEY_ATTR_8021Q, &q_key, sizeof(q_key));
}
+ base->vlan_tci = new_tci;
+}
- if (base->nw_tos != flow->nw_tos) {
- nl_msg_put_u8(odp_actions, OVS_ACTION_ATTR_SET_NW_TOS, flow->nw_tos);
- base->nw_tos = flow->nw_tos;
+static void
+commit_set_nw_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions)
+{
+ struct ovs_key_ipv4 ipv4_key;
+
+ if (base->dl_type != htons(ETH_TYPE_IP) ||
+ !base->nw_src || !base->nw_dst) {
+ return;
+ }
+
+ if (base->nw_src == flow->nw_src &&
+ base->nw_dst == flow->nw_dst &&
+ base->nw_tos == flow->nw_tos) {
+ return;
}
- commit_vlan_tci(ctx, flow->vlan_tci);
+ memset(&ipv4_key, 0, sizeof(ipv4_key));
+ ipv4_key.ipv4_src = base->nw_src = flow->nw_src;
+ ipv4_key.ipv4_dst = base->nw_dst = flow->nw_dst;
+ ipv4_key.ipv4_tos = base->nw_tos = flow->nw_tos;
- if (base->tp_src != flow->tp_src) {
- nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_TP_SRC, flow->tp_src);
- base->tp_src = flow->tp_src;
+ ipv4_key.ipv4_proto = base->nw_proto;
+
+ commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
+ OVS_KEY_ATTR_IPV4, &ipv4_key, sizeof(ipv4_key));
+}
+
+static void
+commit_set_port_action(const struct flow *flow, struct flow *base,
+ struct ofpbuf *odp_actions)
+{
+ if (!base->tp_src || !base->tp_dst) {
+ return;
}
- if (base->tp_dst != flow->tp_dst) {
- nl_msg_put_be16(odp_actions, OVS_ACTION_ATTR_SET_TP_DST, flow->tp_dst);
- base->tp_dst = flow->tp_dst;
+ if (base->tp_src == flow->tp_src &&
+ base->tp_dst == flow->tp_dst) {
+ return;
}
- if (!eth_addr_equals(base->dl_src, flow->dl_src)) {
- nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_SET_DL_SRC,
- flow->dl_src, ETH_ADDR_LEN);
- memcpy(base->dl_src, flow->dl_src, ETH_ADDR_LEN);
+ if (flow->nw_proto == IPPROTO_TCP) {
+ struct ovs_key_tcp port_key;
+
+ port_key.tcp_src = base->tp_src = flow->tp_src;
+ port_key.tcp_dst = base->tp_dst = flow->tp_dst;
+
+ commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
+ OVS_KEY_ATTR_TCP, &port_key, sizeof(port_key));
+
+ } else if (flow->nw_proto == IPPROTO_UDP) {
+ struct ovs_key_udp port_key;
+
+ port_key.udp_src = base->tp_src = flow->tp_src;
+ port_key.udp_dst = base->tp_dst = flow->tp_dst;
+
+ commit_action__(odp_actions, OVS_ACTION_ATTR_SET,
+ OVS_KEY_ATTR_UDP, &port_key, sizeof(port_key));
}
+}
- if (!eth_addr_equals(base->dl_dst, flow->dl_dst)) {
- nl_msg_put_unspec(odp_actions, OVS_ACTION_ATTR_SET_DL_DST,
- flow->dl_dst, ETH_ADDR_LEN);
- memcpy(base->dl_dst, flow->dl_dst, ETH_ADDR_LEN);
+static void
+commit_priority_action(struct action_xlate_ctx *ctx)
+{
+ if (ctx->base_priority == ctx->priority) {
+ return;
}
- if (ctx->base_priority != ctx->priority) {
- if (ctx->priority) {
- nl_msg_put_u32(odp_actions, OVS_ACTION_ATTR_SET_PRIORITY,
- ctx->priority);
- } else {
- nl_msg_put_flag(odp_actions, OVS_ACTION_ATTR_POP_PRIORITY);
- }
- ctx->base_priority = ctx->priority;
+ if (ctx->priority) {
+ nl_msg_put_u32(ctx->odp_actions,
+ OVS_ACTION_ATTR_SET_PRIORITY, ctx->priority);
+ } else {
+ nl_msg_put_flag(ctx->odp_actions, OVS_ACTION_ATTR_POP_PRIORITY);
}
+ ctx->base_priority = ctx->priority;
+}
+
+static void
+commit_odp_actions(struct action_xlate_ctx *ctx)
+{
+ const struct flow *flow = &ctx->flow;
+ struct flow *base = &ctx->base_flow;
+ struct ofpbuf *odp_actions = ctx->odp_actions;
+
+ commit_set_tun_id_action(flow, base, odp_actions);
+ commit_set_ether_addr_action(flow, base, odp_actions);
+ commit_vlan_action(ctx, flow->vlan_tci);
+ commit_set_nw_action(flow, base, odp_actions);
+ commit_set_port_action(flow, base, odp_actions);
+ commit_priority_action(ctx);
}
static void
if (tci) {
tci |= htons(VLAN_CFI);
}
- commit_vlan_tci(ctx, tci);
+ commit_vlan_action(ctx, tci);
cur_vid = dst->vid;
}
AT_CHECK([ovs-ofctl add-flows br0 flows.txt])
AT_CHECK([ovs-appctl -t test-openflowd ofproto/trace br0 'tun_id(0x1),in_port(90),eth(src=50:54:00:00:00:05,dst=50:54:00:00:00:07),eth_type(0x0800),ipv4(src=192.168.0.1,dst=192.168.0.2,proto=1,tos=0),icmp(type=8,code=0)'], [0], [stdout])
AT_CHECK([tail -1 stdout], [0],
- [Datapath actions: set_tunnel(0x1),1,2,set_tunnel(0x3),3,4
+ [Datapath actions: set(tun_id(0x1)),1,2,set(tun_id(0x3)),3,4
])
OFPROTO_STOP
AT_CLEANUP
"br0 none 0 drop" \
"br0 0 0 drop" \
"br0 0 1 drop" \
- "br0 10 0 p1,p5,p6,p7,p8,pop_vlan,p2" \
- "br0 10 1 p1,p5,p6,p7,p8,pop_vlan,push_vlan(vid=0,pcp=1),p2" \
+ "br0 10 0 p1,p5,p6,p7,p8,pop(vlan),p2" \
+ "br0 10 1 p1,p5,p6,p7,p8,pop(vlan),push(vlan(vid=0,pcp=1)),p2" \
"br0 11 0 p5,p7" \
"br0 11 1 p5,p7" \
- "br0 12 0 p1,p5,p6,pop_vlan,p3,p4,p7,p8" \
- "br0 12 1 p1,p5,p6,pop_vlan,push_vlan(vid=0,pcp=1),p3,p4,p7,p8" \
+ "br0 12 0 p1,p5,p6,pop(vlan),p3,p4,p7,p8" \
+ "br0 12 1 p1,p5,p6,pop(vlan),push(vlan(vid=0,pcp=1)),p3,p4,p7,p8" \
"p1 none 0 drop" \
"p1 0 0 drop" \
"p1 0 1 drop" \
- "p1 10 0 br0,p5,p6,p7,p8,pop_vlan,p2" \
- "p1 10 1 br0,p5,p6,p7,p8,pop_vlan,push_vlan(vid=0,pcp=1),p2" \
+ "p1 10 0 br0,p5,p6,p7,p8,pop(vlan),p2" \
+ "p1 10 1 br0,p5,p6,p7,p8,pop(vlan),push(vlan(vid=0,pcp=1)),p2" \
"p1 11 0 drop" \
"p1 11 1 drop" \
- "p1 12 0 br0,p5,p6,pop_vlan,p3,p4,p7,p8" \
- "p1 12 1 br0,p5,p6,pop_vlan,push_vlan(vid=0,pcp=1),p3,p4,p7,p8" \
- "p2 none 0 push_vlan(vid=10,pcp=0),br0,p1,p5,p6,p7,p8" \
- "p2 0 0 pop_vlan,push_vlan(vid=10,pcp=0),br0,p1,p5,p6,p7,p8" \
- "p2 0 1 pop_vlan,push_vlan(vid=10,pcp=1),br0,p1,p5,p6,p7,p8" \
+ "p1 12 0 br0,p5,p6,pop(vlan),p3,p4,p7,p8" \
+ "p1 12 1 br0,p5,p6,pop(vlan),push(vlan(vid=0,pcp=1)),p3,p4,p7,p8" \
+ "p2 none 0 push(vlan(vid=10,pcp=0)),br0,p1,p5,p6,p7,p8" \
+ "p2 0 0 pop(vlan),push(vlan(vid=10,pcp=0)),br0,p1,p5,p6,p7,p8" \
+ "p2 0 1 pop(vlan),push(vlan(vid=10,pcp=1)),br0,p1,p5,p6,p7,p8" \
"p2 10 0 drop" \
"p2 10 1 drop" \
"p2 11 0 drop" \
"p2 11 1 drop" \
"p2 12 0 drop" \
"p2 12 1 drop" \
- "p3 none 0 p4,p7,p8,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
- "p3 0 0 p4,p7,p8,pop_vlan,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
- "p3 0 1 p4,p7,p8,pop_vlan,push_vlan(vid=12,pcp=1),br0,p1,p5,p6" \
+ "p3 none 0 p4,p7,p8,push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
+ "p3 0 0 p4,p7,p8,pop(vlan),push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
+ "p3 0 1 p4,p7,p8,pop(vlan),push(vlan(vid=12,pcp=1)),br0,p1,p5,p6" \
"p3 10 0 drop" \
"p3 10 1 drop" \
"p3 11 0 drop" \
"p3 11 1 drop" \
"p3 12 0 drop" \
"p3 12 1 drop" \
- "p4 none 0 p3,p7,p8,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
- "p4 0 0 p3,p7,p8,pop_vlan,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
- "p4 0 1 p3,p7,p8,pop_vlan,push_vlan(vid=12,pcp=1),br0,p1,p5,p6" \
+ "p4 none 0 p3,p7,p8,push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
+ "p4 0 0 p3,p7,p8,pop(vlan),push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
+ "p4 0 1 p3,p7,p8,pop(vlan),push(vlan(vid=12,pcp=1)),br0,p1,p5,p6" \
"p4 10 0 drop" \
"p4 10 1 drop" \
"p4 11 0 drop" \
"p4 11 1 drop" \
"p4 12 0 drop" \
"p4 12 1 drop" \
- "p5 none 0 p2,push_vlan(vid=10,pcp=0),br0,p1,p6,p7,p8" \
- "p5 0 0 p2,pop_vlan,push_vlan(vid=10,pcp=0),br0,p1,p6,p7,p8" \
- "p5 0 1 p2,pop_vlan,push_vlan(vid=10,pcp=1),br0,p1,p6,p7,p8" \
- "p5 10 0 br0,p1,p6,p7,p8,pop_vlan,p2" \
- "p5 10 1 br0,p1,p6,p7,p8,pop_vlan,push_vlan(vid=0,pcp=1),p2" \
+ "p5 none 0 p2,push(vlan(vid=10,pcp=0)),br0,p1,p6,p7,p8" \
+ "p5 0 0 p2,pop(vlan),push(vlan(vid=10,pcp=0)),br0,p1,p6,p7,p8" \
+ "p5 0 1 p2,pop(vlan),push(vlan(vid=10,pcp=1)),br0,p1,p6,p7,p8" \
+ "p5 10 0 br0,p1,p6,p7,p8,pop(vlan),p2" \
+ "p5 10 1 br0,p1,p6,p7,p8,pop(vlan),push(vlan(vid=0,pcp=1)),p2" \
"p5 11 0 br0,p7" \
"p5 11 1 br0,p7" \
- "p5 12 0 br0,p1,p6,pop_vlan,p3,p4,p7,p8" \
- "p5 12 1 br0,p1,p6,pop_vlan,push_vlan(vid=0,pcp=1),p3,p4,p7,p8" \
- "p6 none 0 p2,push_vlan(vid=10,pcp=0),br0,p1,p5,p7,p8" \
- "p6 0 0 p2,pop_vlan,push_vlan(vid=10,pcp=0),br0,p1,p5,p7,p8" \
- "p6 0 1 p2,pop_vlan,push_vlan(vid=10,pcp=1),br0,p1,p5,p7,p8" \
- "p6 10 0 br0,p1,p5,p7,p8,pop_vlan,p2" \
- "p6 10 1 br0,p1,p5,p7,p8,pop_vlan,push_vlan(vid=0,pcp=1),p2" \
+ "p5 12 0 br0,p1,p6,pop(vlan),p3,p4,p7,p8" \
+ "p5 12 1 br0,p1,p6,pop(vlan),push(vlan(vid=0,pcp=1)),p3,p4,p7,p8" \
+ "p6 none 0 p2,push(vlan(vid=10,pcp=0)),br0,p1,p5,p7,p8" \
+ "p6 0 0 p2,pop(vlan),push(vlan(vid=10,pcp=0)),br0,p1,p5,p7,p8" \
+ "p6 0 1 p2,pop(vlan),push(vlan(vid=10,pcp=1)),br0,p1,p5,p7,p8" \
+ "p6 10 0 br0,p1,p5,p7,p8,pop(vlan),p2" \
+ "p6 10 1 br0,p1,p5,p7,p8,pop(vlan),push(vlan(vid=0,pcp=1)),p2" \
"p6 11 0 drop" \
"p6 11 1 drop" \
- "p6 12 0 br0,p1,p5,pop_vlan,p3,p4,p7,p8" \
- "p6 12 1 br0,p1,p5,pop_vlan,push_vlan(vid=0,pcp=1),p3,p4,p7,p8" \
- "p7 none 0 p3,p4,p8,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
- "p7 0 0 p3,p4,p8,pop_vlan,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
- "p7 0 1 p3,p4,p8,pop_vlan,push_vlan(vid=12,pcp=1),br0,p1,p5,p6" \
- "p7 10 0 br0,p1,p5,p6,p8,pop_vlan,p2" \
- "p7 10 1 br0,p1,p5,p6,p8,pop_vlan,push_vlan(vid=0,pcp=1),p2" \
+ "p6 12 0 br0,p1,p5,pop(vlan),p3,p4,p7,p8" \
+ "p6 12 1 br0,p1,p5,pop(vlan),push(vlan(vid=0,pcp=1)),p3,p4,p7,p8" \
+ "p7 none 0 p3,p4,p8,push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
+ "p7 0 0 p3,p4,p8,pop(vlan),push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
+ "p7 0 1 p3,p4,p8,pop(vlan),push(vlan(vid=12,pcp=1)),br0,p1,p5,p6" \
+ "p7 10 0 br0,p1,p5,p6,p8,pop(vlan),p2" \
+ "p7 10 1 br0,p1,p5,p6,p8,pop(vlan),push(vlan(vid=0,pcp=1)),p2" \
"p7 11 0 br0,p5" \
"p7 11 1 br0,p5" \
- "p7 12 0 br0,p1,p5,p6,pop_vlan,p3,p4,p8" \
- "p7 12 1 br0,p1,p5,p6,pop_vlan,push_vlan(vid=0,pcp=1),p3,p4,p8" \
- "p8 none 0 p3,p4,p7,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
- "p8 0 0 p3,p4,p7,pop_vlan,push_vlan(vid=12,pcp=0),br0,p1,p5,p6" \
- "p8 0 1 p3,p4,p7,pop_vlan,push_vlan(vid=12,pcp=1),br0,p1,p5,p6" \
- "p8 10 0 br0,p1,p5,p6,p7,pop_vlan,p2" \
- "p8 10 1 br0,p1,p5,p6,p7,pop_vlan,push_vlan(vid=0,pcp=1),p2" \
+ "p7 12 0 br0,p1,p5,p6,pop(vlan),p3,p4,p8" \
+ "p7 12 1 br0,p1,p5,p6,pop(vlan),push(vlan(vid=0,pcp=1)),p3,p4,p8" \
+ "p8 none 0 p3,p4,p7,push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
+ "p8 0 0 p3,p4,p7,pop(vlan),push(vlan(vid=12,pcp=0)),br0,p1,p5,p6" \
+ "p8 0 1 p3,p4,p7,pop(vlan),push(vlan(vid=12,pcp=1)),br0,p1,p5,p6" \
+ "p8 10 0 br0,p1,p5,p6,p7,pop(vlan),p2" \
+ "p8 10 1 br0,p1,p5,p6,p7,pop(vlan),push(vlan(vid=0,pcp=1)),p2" \
"p8 11 0 drop" \
"p8 11 1 drop" \
- "p8 12 0 br0,p1,p5,p6,pop_vlan,p3,p4,p7" \
- "p8 12 1 br0,p1,p5,p6,pop_vlan,push_vlan(vid=0,pcp=1),p3,p4,p7"
+ "p8 12 0 br0,p1,p5,p6,pop(vlan),p3,p4,p7" \
+ "p8 12 1 br0,p1,p5,p6,pop(vlan),push(vlan(vid=0,pcp=1)),p3,p4,p7"
do
set $tuple
in_port=$1