Following match makes necessary changes required in ovs kernel module to handle new format of actions list passed from user-space. Validation and execution paths are changed.
Signed-off-by: Pravin B Shelar <pshe...@nicira.com> Bug #7115 --- datapath/actions.c | 191 ++++++++++++++++++++++++++++----------------------- datapath/datapath.c | 129 +++++++++++++++++++++-------------- datapath/flow.c | 2 +- 3 files changed, 183 insertions(+), 139 deletions(-) diff --git a/datapath/actions.c b/datapath/actions.c index 945c461..f619e43 100644 --- a/datapath/actions.c +++ b/datapath/actions.c @@ -117,113 +117,128 @@ static int push_vlan(struct sk_buff *skb, __be16 new_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; + + if (memcmp(eth_hdr(skb)->h_source, eth_key->eth_src, ETH_HLEN)) + memcpy(eth_hdr(skb)->h_source, eth_key->eth_src, ETH_HLEN); + if (memcmp(eth_hdr(skb)->h_dest, eth_key->eth_dst, 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 (likely(transport_len >= sizeof(struct tcphdr))) - return &tcp_hdr(skb)->check; + inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb, + *addr, new_addr, 1); } else if (nw_proto == 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; + return err; } -static int set_nw_tos(struct sk_buff *skb, u8 nw_tos) +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; + /* Must follow make_writable() since that can move the skb data. */ + 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; } @@ -295,6 +310,12 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, for (a = attr, rem = len; rem > 0; a = nla_next(a, &rem)) { + const struct ovs_key_8021q *q_key; + const struct ovs_key_tcp *tcp_port_key; + const struct ovs_key_udp *udp_port_key; + const struct ovs_key_ipv4 *ipv4_key; + const struct ovs_key_ethernet *eth_key; + int err = 0; if (prev_port != -1) { @@ -303,63 +324,61 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, } switch (nla_type(a)) { - case OVS_ACTION_ATTR_OUTPUT: + case OVS_ACT_TYPE(OVS_ACTION_ATTR_OUTPUT, OVS_KEY_ATTR_NONE): prev_port = nla_get_u32(a); break; - case OVS_ACTION_ATTR_USERSPACE: + case OVS_ACT_TYPE(OVS_ACTION_ATTR_USERSPACE, + OVS_KEY_ATTR_NONE): output_userspace(dp, skb, nla_get_u64(a)); break; - case OVS_ACTION_ATTR_SET_TUNNEL: + case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TUN_ID): OVS_CB(skb)->tun_id = nla_get_be64(a); break; - case OVS_ACTION_ATTR_PUSH_VLAN: - err = push_vlan(skb, nla_get_be16(a)); + case OVS_ACT_TYPE(OVS_ACTION_ATTR_PUSH, OVS_KEY_ATTR_8021Q): + q_key = nla_data(a); + err = push_vlan(skb, q_key->q_tci); if (unlikely(err)) /* skb already freed */ return err; break; - case OVS_ACTION_ATTR_POP_VLAN: + case OVS_ACT_TYPE(OVS_ACTION_ATTR_POP, OVS_KEY_ATTR_8021Q): 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); + case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_ETHERNET): + eth_key = nla_data(a); + err = set_eth_addr(skb, eth_key); break; - case OVS_ACTION_ATTR_SET_NW_SRC: - case OVS_ACTION_ATTR_SET_NW_DST: - err = set_nw_addr(skb, a); + case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TCP): + tcp_port_key = nla_data(a); + err = set_tcp_port(skb, tcp_port_key); break; - case OVS_ACTION_ATTR_SET_NW_TOS: - err = set_nw_tos(skb, nla_get_u8(a)); + case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_UDP): + udp_port_key = nla_data(a); + err = set_udp_port(skb, udp_port_key); break; - case OVS_ACTION_ATTR_SET_TP_SRC: - case OVS_ACTION_ATTR_SET_TP_DST: - err = set_tp_port(skb, a); + case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_IPV4): + ipv4_key = nla_data(a); + err = set_ipv4(skb, ipv4_key); break; - case OVS_ACTION_ATTR_SET_PRIORITY: + case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET_PRIORITY, + OVS_KEY_ATTR_NONE): skb->priority = nla_get_u32(a); break; - case OVS_ACTION_ATTR_POP_PRIORITY: + case OVS_ACT_TYPE(OVS_ACTION_ATTR_RESTORE_PRIORITY, + OVS_KEY_ATTR_NONE): skb->priority = priority; break; - case OVS_ACTION_ATTR_SAMPLE: + case OVS_ACT_TYPE(OVS_ACTION_ATTR_SAMPLE, OVS_KEY_ATTR_NONE): err = sample(dp, skb, a); break; diff --git a/datapath/datapath.c b/datapath/datapath.c index 07188ad..001f6f4 100644 --- a/datapath/datapath.c +++ b/datapath/datapath.c @@ -499,9 +499,11 @@ static int flush_flows(int dp_ifindex) 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] = { @@ -520,10 +522,11 @@ static int validate_sample(const struct nlattr *attr, int depth) 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_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; @@ -532,67 +535,88 @@ static int validate_actions(const struct nlattr *attr, int depth) return -EOVERFLOW; nla_for_each_nested(a, attr, rem) { - static const u32 action_lens[OVS_ACTION_ATTR_MAX + 1] = { - [OVS_ACTION_ATTR_OUTPUT] = 4, - [OVS_ACTION_ATTR_USERSPACE] = 8, - [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_SET_PRIORITY] = 4, - [OVS_ACTION_ATTR_POP_PRIORITY] = 0, - }; - int type = nla_type(a); + const struct ovs_key_8021q *q_key; + const struct ovs_key_ipv4 *ipv4_key; - /* Match expected attr len for given attr len except for - * OVS_ACTION_ATTR_SAMPLE, as it has nested actions list which - * is variable size. */ - if (type > OVS_ACTION_ATTR_MAX || - (nla_len(a) != action_lens[type] && - type != OVS_ACTION_ATTR_SAMPLE)) - return -EINVAL; + int type = nla_type(a); switch (type) { - case OVS_ACTION_ATTR_UNSPEC: + case OVS_ACT_TYPE(OVS_ACTION_ATTR_UNSPEC, OVS_KEY_ATTR_NONE): return -EINVAL; - case OVS_ACTION_ATTR_USERSPACE: - 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. */ + case OVS_ACT_TYPE(OVS_ACTION_ATTR_USERSPACE, + OVS_KEY_ATTR_NONE): + case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TUN_ID): + if (nla_len(a) != sizeof(u64)) + return -EINVAL; + break; + + case OVS_ACT_TYPE(OVS_ACTION_ATTR_POP, OVS_KEY_ATTR_8021Q): + case OVS_ACT_TYPE(OVS_ACTION_ATTR_RESTORE_PRIORITY, + OVS_KEY_ATTR_NONE): + if (nla_len(a) != 0) + return -EINVAL; + break; + + case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET_PRIORITY, + OVS_KEY_ATTR_NONE): + if (nla_len(a) != sizeof(u32)) + return -EINVAL; break; - case OVS_ACTION_ATTR_OUTPUT: + case OVS_ACT_TYPE(OVS_ACTION_ATTR_OUTPUT, OVS_KEY_ATTR_NONE): + if (nla_len(a) != sizeof(u32)) + return -EINVAL; + if (nla_get_u32(a) >= DP_MAX_PORTS) return -EINVAL; break; - case OVS_ACTION_ATTR_PUSH_VLAN: - if (nla_get_be16(a) & htons(VLAN_CFI_MASK)) + case OVS_ACT_TYPE(OVS_ACTION_ATTR_PUSH, OVS_KEY_ATTR_8021Q): + if (nla_len(a) != sizeof(*q_key)) + return -EINVAL; + + q_key = nla_data(a); + if (q_key->q_tci & htons(VLAN_CFI_MASK)) return -EINVAL; break; - case OVS_ACTION_ATTR_SET_NW_TOS: - if (nla_get_u8(a) & INET_ECN_MASK) + case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_IPV4): + if (nla_len(a) != sizeof(*ipv4_key)) + return -EINVAL; + if (key->eth.type != htons(ETH_P_IP)) + return -EINVAL; + + ipv4_key = nla_data(a); + + if (ipv4_key->ipv4_tos & INET_ECN_MASK) return -EINVAL; break; - case OVS_ACTION_ATTR_SAMPLE: - err = validate_sample(a, depth); + case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_ETHERNET): + if (nla_len(a) != sizeof(struct ovs_key_ethernet)) + return -EINVAL; + + break; + + case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_TCP): + if (nla_len(a) != sizeof(struct ovs_key_tcp)) + return -EINVAL; + if (key->ip.proto != IPPROTO_TCP) + return -EINVAL; + + break; + + case OVS_ACT_TYPE(OVS_ACTION_ATTR_SET, OVS_KEY_ATTR_UDP): + if (nla_len(a) != sizeof(struct ovs_key_udp)) + return -EINVAL; + if (key->ip.proto != IPPROTO_UDP) + return -EINVAL; + + break; + + case OVS_ACT_TYPE(OVS_ACTION_ATTR_SAMPLE, OVS_KEY_ATTR_NONE): + err = validate_sample(a, key, depth); if (err) return err; break; @@ -635,9 +659,6 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) 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); @@ -675,6 +696,10 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) 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); if (a[OVS_PACKET_ATTR_UPCALL_PID]) @@ -904,7 +929,7 @@ static int ovs_flow_cmd_new_or_set(struct sk_buff *skb, struct genl_info *info) /* 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) { diff --git a/datapath/flow.c b/datapath/flow.c index 7322295..5d8573e 100644 --- a/datapath/flow.c +++ b/datapath/flow.c @@ -1103,7 +1103,7 @@ int flow_to_nlattrs(const struct sw_flow_key *swkey, struct sk_buff *skb) /* This is an imperfect sanity-check that FLOW_BUFSIZE doesn't need * to be updated, but will at least raise awareness when new * datapath key types are added. */ - BUILD_BUG_ON(__OVS_KEY_ATTR_MAX != 14); + BUILD_BUG_ON(__OVS_KEY_ATTR_MAX != 15); if (swkey->eth.tun_id != cpu_to_be64(0)) NLA_PUT_BE64(skb, OVS_KEY_ATTR_TUN_ID, swkey->eth.tun_id); -- 1.7.1 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev