diff options
| author | William Tu <u9012063@gmail.com> | 2016-06-10 14:49:33 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2016-06-10 20:58:03 -0400 |
| commit | f2a4d086ed4c588d32fe9b7aa67fead7280e7bf1 (patch) | |
| tree | 7824d6446b4fd259cc7430641fd27475445361ba /net/openvswitch | |
| parent | 1578b0a5e92825334760741e5c166b8873886f1b (diff) | |
openvswitch: Add packet truncation support.
The patch adds a new OVS action, OVS_ACTION_ATTR_TRUNC, in order to
truncate packets. A 'max_len' is added for setting up the maximum
packet size, and a 'cutlen' field is to record the number of bytes
to trim the packet when the packet is outputting to a port, or when
the packet is sent to userspace.
Signed-off-by: William Tu <u9012063@gmail.com>
Cc: Pravin Shelar <pshelar@nicira.com>
Acked-by: Pravin B Shelar <pshelar@ovn.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/openvswitch')
| -rw-r--r-- | net/openvswitch/actions.c | 40 | ||||
| -rw-r--r-- | net/openvswitch/datapath.c | 29 | ||||
| -rw-r--r-- | net/openvswitch/datapath.h | 5 | ||||
| -rw-r--r-- | net/openvswitch/flow_netlink.c | 9 | ||||
| -rw-r--r-- | net/openvswitch/vport.c | 1 |
5 files changed, 67 insertions, 17 deletions
diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 9a3eb7a0ebf4..1ecbd7715f6d 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c | |||
| @@ -750,6 +750,14 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, | |||
| 750 | 750 | ||
| 751 | if (likely(vport)) { | 751 | if (likely(vport)) { |
| 752 | u16 mru = OVS_CB(skb)->mru; | 752 | u16 mru = OVS_CB(skb)->mru; |
| 753 | u32 cutlen = OVS_CB(skb)->cutlen; | ||
| 754 | |||
| 755 | if (unlikely(cutlen > 0)) { | ||
| 756 | if (skb->len - cutlen > ETH_HLEN) | ||
| 757 | pskb_trim(skb, skb->len - cutlen); | ||
| 758 | else | ||
| 759 | pskb_trim(skb, ETH_HLEN); | ||
| 760 | } | ||
| 753 | 761 | ||
| 754 | if (likely(!mru || (skb->len <= mru + ETH_HLEN))) { | 762 | if (likely(!mru || (skb->len <= mru + ETH_HLEN))) { |
| 755 | ovs_vport_send(vport, skb); | 763 | ovs_vport_send(vport, skb); |
| @@ -775,7 +783,8 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port, | |||
| 775 | 783 | ||
| 776 | static int output_userspace(struct datapath *dp, struct sk_buff *skb, | 784 | static int output_userspace(struct datapath *dp, struct sk_buff *skb, |
| 777 | struct sw_flow_key *key, const struct nlattr *attr, | 785 | struct sw_flow_key *key, const struct nlattr *attr, |
| 778 | const struct nlattr *actions, int actions_len) | 786 | const struct nlattr *actions, int actions_len, |
| 787 | uint32_t cutlen) | ||
| 779 | { | 788 | { |
| 780 | struct dp_upcall_info upcall; | 789 | struct dp_upcall_info upcall; |
| 781 | const struct nlattr *a; | 790 | const struct nlattr *a; |
| @@ -822,7 +831,7 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb, | |||
| 822 | } /* End of switch. */ | 831 | } /* End of switch. */ |
| 823 | } | 832 | } |
| 824 | 833 | ||
| 825 | return ovs_dp_upcall(dp, skb, key, &upcall); | 834 | return ovs_dp_upcall(dp, skb, key, &upcall, cutlen); |
| 826 | } | 835 | } |
| 827 | 836 | ||
| 828 | static int sample(struct datapath *dp, struct sk_buff *skb, | 837 | static int sample(struct datapath *dp, struct sk_buff *skb, |
| @@ -832,6 +841,7 @@ static int sample(struct datapath *dp, struct sk_buff *skb, | |||
| 832 | const struct nlattr *acts_list = NULL; | 841 | const struct nlattr *acts_list = NULL; |
| 833 | const struct nlattr *a; | 842 | const struct nlattr *a; |
| 834 | int rem; | 843 | int rem; |
| 844 | u32 cutlen = 0; | ||
| 835 | 845 | ||
| 836 | for (a = nla_data(attr), rem = nla_len(attr); rem > 0; | 846 | for (a = nla_data(attr), rem = nla_len(attr); rem > 0; |
| 837 | a = nla_next(a, &rem)) { | 847 | a = nla_next(a, &rem)) { |
| @@ -858,13 +868,24 @@ static int sample(struct datapath *dp, struct sk_buff *skb, | |||
| 858 | return 0; | 868 | return 0; |
| 859 | 869 | ||
| 860 | /* The only known usage of sample action is having a single user-space | 870 | /* The only known usage of sample action is having a single user-space |
| 871 | * action, or having a truncate action followed by a single user-space | ||
| 861 | * action. Treat this usage as a special case. | 872 | * action. Treat this usage as a special case. |
| 862 | * The output_userspace() should clone the skb to be sent to the | 873 | * The output_userspace() should clone the skb to be sent to the |
| 863 | * user space. This skb will be consumed by its caller. | 874 | * user space. This skb will be consumed by its caller. |
| 864 | */ | 875 | */ |
| 876 | if (unlikely(nla_type(a) == OVS_ACTION_ATTR_TRUNC)) { | ||
| 877 | struct ovs_action_trunc *trunc = nla_data(a); | ||
| 878 | |||
| 879 | if (skb->len > trunc->max_len) | ||
| 880 | cutlen = skb->len - trunc->max_len; | ||
| 881 | |||
| 882 | a = nla_next(a, &rem); | ||
| 883 | } | ||
| 884 | |||
| 865 | if (likely(nla_type(a) == OVS_ACTION_ATTR_USERSPACE && | 885 | if (likely(nla_type(a) == OVS_ACTION_ATTR_USERSPACE && |
| 866 | nla_is_last(a, rem))) | 886 | nla_is_last(a, rem))) |
| 867 | return output_userspace(dp, skb, key, a, actions, actions_len); | 887 | return output_userspace(dp, skb, key, a, actions, |
| 888 | actions_len, cutlen); | ||
| 868 | 889 | ||
| 869 | skb = skb_clone(skb, GFP_ATOMIC); | 890 | skb = skb_clone(skb, GFP_ATOMIC); |
| 870 | if (!skb) | 891 | if (!skb) |
| @@ -1051,6 +1072,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, | |||
| 1051 | if (out_skb) | 1072 | if (out_skb) |
| 1052 | do_output(dp, out_skb, prev_port, key); | 1073 | do_output(dp, out_skb, prev_port, key); |
| 1053 | 1074 | ||
| 1075 | OVS_CB(skb)->cutlen = 0; | ||
| 1054 | prev_port = -1; | 1076 | prev_port = -1; |
| 1055 | } | 1077 | } |
| 1056 | 1078 | ||
| @@ -1059,8 +1081,18 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, | |||
| 1059 | prev_port = nla_get_u32(a); | 1081 | prev_port = nla_get_u32(a); |
| 1060 | break; | 1082 | break; |
| 1061 | 1083 | ||
| 1084 | case OVS_ACTION_ATTR_TRUNC: { | ||
| 1085 | struct ovs_action_trunc *trunc = nla_data(a); | ||
| 1086 | |||
| 1087 | if (skb->len > trunc->max_len) | ||
| 1088 | OVS_CB(skb)->cutlen = skb->len - trunc->max_len; | ||
| 1089 | break; | ||
| 1090 | } | ||
| 1091 | |||
| 1062 | case OVS_ACTION_ATTR_USERSPACE: | 1092 | case OVS_ACTION_ATTR_USERSPACE: |
| 1063 | output_userspace(dp, skb, key, a, attr, len); | 1093 | output_userspace(dp, skb, key, a, attr, |
| 1094 | len, OVS_CB(skb)->cutlen); | ||
| 1095 | OVS_CB(skb)->cutlen = 0; | ||
| 1064 | break; | 1096 | break; |
| 1065 | 1097 | ||
| 1066 | case OVS_ACTION_ATTR_HASH: | 1098 | case OVS_ACTION_ATTR_HASH: |
diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 856bd8dba676..673934295333 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c | |||
| @@ -137,10 +137,12 @@ EXPORT_SYMBOL_GPL(lockdep_ovsl_is_held); | |||
| 137 | static struct vport *new_vport(const struct vport_parms *); | 137 | static struct vport *new_vport(const struct vport_parms *); |
| 138 | static int queue_gso_packets(struct datapath *dp, struct sk_buff *, | 138 | static int queue_gso_packets(struct datapath *dp, struct sk_buff *, |
| 139 | const struct sw_flow_key *, | 139 | const struct sw_flow_key *, |
| 140 | const struct dp_upcall_info *); | 140 | const struct dp_upcall_info *, |
| 141 | uint32_t cutlen); | ||
| 141 | static int queue_userspace_packet(struct datapath *dp, struct sk_buff *, | 142 | static int queue_userspace_packet(struct datapath *dp, struct sk_buff *, |
| 142 | const struct sw_flow_key *, | 143 | const struct sw_flow_key *, |
| 143 | const struct dp_upcall_info *); | 144 | const struct dp_upcall_info *, |
| 145 | uint32_t cutlen); | ||
| 144 | 146 | ||
| 145 | /* Must be called with rcu_read_lock. */ | 147 | /* Must be called with rcu_read_lock. */ |
| 146 | static struct datapath *get_dp_rcu(struct net *net, int dp_ifindex) | 148 | static struct datapath *get_dp_rcu(struct net *net, int dp_ifindex) |
| @@ -275,7 +277,7 @@ void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key) | |||
| 275 | upcall.cmd = OVS_PACKET_CMD_MISS; | 277 | upcall.cmd = OVS_PACKET_CMD_MISS; |
| 276 | upcall.portid = ovs_vport_find_upcall_portid(p, skb); | 278 | upcall.portid = ovs_vport_find_upcall_portid(p, skb); |
| 277 | upcall.mru = OVS_CB(skb)->mru; | 279 | upcall.mru = OVS_CB(skb)->mru; |
| 278 | error = ovs_dp_upcall(dp, skb, key, &upcall); | 280 | error = ovs_dp_upcall(dp, skb, key, &upcall, 0); |
| 279 | if (unlikely(error)) | 281 | if (unlikely(error)) |
| 280 | kfree_skb(skb); | 282 | kfree_skb(skb); |
| 281 | else | 283 | else |
| @@ -300,7 +302,8 @@ out: | |||
| 300 | 302 | ||
| 301 | int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, | 303 | int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, |
| 302 | const struct sw_flow_key *key, | 304 | const struct sw_flow_key *key, |
| 303 | const struct dp_upcall_info *upcall_info) | 305 | const struct dp_upcall_info *upcall_info, |
| 306 | uint32_t cutlen) | ||
| 304 | { | 307 | { |
| 305 | struct dp_stats_percpu *stats; | 308 | struct dp_stats_percpu *stats; |
| 306 | int err; | 309 | int err; |
| @@ -311,9 +314,9 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb, | |||
| 311 | } | 314 | } |
| 312 | 315 | ||
| 313 | if (!skb_is_gso(skb)) | 316 | if (!skb_is_gso(skb)) |
| 314 | err = queue_userspace_packet(dp, skb, key, upcall_info); | 317 | err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen); |
| 315 | else | 318 | else |
| 316 | err = queue_gso_packets(dp, skb, key, upcall_info); | 319 | err = queue_gso_packets(dp, skb, key, upcall_info, cutlen); |
| 317 | if (err) | 320 | if (err) |
| 318 | goto err; | 321 | goto err; |
| 319 | 322 | ||
| @@ -331,7 +334,8 @@ err: | |||
| 331 | 334 | ||
| 332 | static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb, | 335 | static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb, |
| 333 | const struct sw_flow_key *key, | 336 | const struct sw_flow_key *key, |
| 334 | const struct dp_upcall_info *upcall_info) | 337 | const struct dp_upcall_info *upcall_info, |
| 338 | uint32_t cutlen) | ||
| 335 | { | 339 | { |
| 336 | unsigned short gso_type = skb_shinfo(skb)->gso_type; | 340 | unsigned short gso_type = skb_shinfo(skb)->gso_type; |
| 337 | struct sw_flow_key later_key; | 341 | struct sw_flow_key later_key; |
| @@ -360,7 +364,7 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb, | |||
| 360 | if (gso_type & SKB_GSO_UDP && skb != segs) | 364 | if (gso_type & SKB_GSO_UDP && skb != segs) |
| 361 | key = &later_key; | 365 | key = &later_key; |
| 362 | 366 | ||
| 363 | err = queue_userspace_packet(dp, skb, key, upcall_info); | 367 | err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen); |
| 364 | if (err) | 368 | if (err) |
| 365 | break; | 369 | break; |
| 366 | 370 | ||
| @@ -416,7 +420,8 @@ static void pad_packet(struct datapath *dp, struct sk_buff *skb) | |||
| 416 | 420 | ||
| 417 | static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, | 421 | static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, |
| 418 | const struct sw_flow_key *key, | 422 | const struct sw_flow_key *key, |
| 419 | const struct dp_upcall_info *upcall_info) | 423 | const struct dp_upcall_info *upcall_info, |
| 424 | uint32_t cutlen) | ||
| 420 | { | 425 | { |
| 421 | struct ovs_header *upcall; | 426 | struct ovs_header *upcall; |
| 422 | struct sk_buff *nskb = NULL; | 427 | struct sk_buff *nskb = NULL; |
| @@ -461,7 +466,7 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, | |||
| 461 | else | 466 | else |
| 462 | hlen = skb->len; | 467 | hlen = skb->len; |
| 463 | 468 | ||
| 464 | len = upcall_msg_size(upcall_info, hlen); | 469 | len = upcall_msg_size(upcall_info, hlen - cutlen); |
| 465 | user_skb = genlmsg_new(len, GFP_ATOMIC); | 470 | user_skb = genlmsg_new(len, GFP_ATOMIC); |
| 466 | if (!user_skb) { | 471 | if (!user_skb) { |
| 467 | err = -ENOMEM; | 472 | err = -ENOMEM; |
| @@ -515,9 +520,9 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb, | |||
| 515 | err = -ENOBUFS; | 520 | err = -ENOBUFS; |
| 516 | goto out; | 521 | goto out; |
| 517 | } | 522 | } |
| 518 | nla->nla_len = nla_attr_size(skb->len); | 523 | nla->nla_len = nla_attr_size(skb->len - cutlen); |
| 519 | 524 | ||
| 520 | err = skb_zerocopy(user_skb, skb, skb->len, hlen); | 525 | err = skb_zerocopy(user_skb, skb, skb->len - cutlen, hlen); |
| 521 | if (err) | 526 | if (err) |
| 522 | goto out; | 527 | goto out; |
| 523 | 528 | ||
diff --git a/net/openvswitch/datapath.h b/net/openvswitch/datapath.h index 427e39a045cf..ab85c1cae255 100644 --- a/net/openvswitch/datapath.h +++ b/net/openvswitch/datapath.h | |||
| @@ -100,11 +100,13 @@ struct datapath { | |||
| 100 | * @input_vport: The original vport packet came in on. This value is cached | 100 | * @input_vport: The original vport packet came in on. This value is cached |
| 101 | * when a packet is received by OVS. | 101 | * when a packet is received by OVS. |
| 102 | * @mru: The maximum received fragement size; 0 if the packet is not | 102 | * @mru: The maximum received fragement size; 0 if the packet is not |
| 103 | * @cutlen: The number of bytes from the packet end to be removed. | ||
| 103 | * fragmented. | 104 | * fragmented. |
| 104 | */ | 105 | */ |
| 105 | struct ovs_skb_cb { | 106 | struct ovs_skb_cb { |
| 106 | struct vport *input_vport; | 107 | struct vport *input_vport; |
| 107 | u16 mru; | 108 | u16 mru; |
| 109 | u32 cutlen; | ||
| 108 | }; | 110 | }; |
| 109 | #define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb) | 111 | #define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb) |
| 110 | 112 | ||
| @@ -194,7 +196,8 @@ extern struct genl_family dp_vport_genl_family; | |||
| 194 | void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key); | 196 | void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key); |
| 195 | void ovs_dp_detach_port(struct vport *); | 197 | void ovs_dp_detach_port(struct vport *); |
| 196 | int ovs_dp_upcall(struct datapath *, struct sk_buff *, | 198 | int ovs_dp_upcall(struct datapath *, struct sk_buff *, |
| 197 | const struct sw_flow_key *, const struct dp_upcall_info *); | 199 | const struct sw_flow_key *, const struct dp_upcall_info *, |
| 200 | uint32_t cutlen); | ||
| 198 | 201 | ||
| 199 | const char *ovs_dp_name(const struct datapath *dp); | 202 | const char *ovs_dp_name(const struct datapath *dp); |
| 200 | struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq, | 203 | struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq, |
diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index 0bb650f4f219..c78a6a1476fb 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c | |||
| @@ -2229,6 +2229,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, | |||
| 2229 | [OVS_ACTION_ATTR_SAMPLE] = (u32)-1, | 2229 | [OVS_ACTION_ATTR_SAMPLE] = (u32)-1, |
| 2230 | [OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash), | 2230 | [OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash), |
| 2231 | [OVS_ACTION_ATTR_CT] = (u32)-1, | 2231 | [OVS_ACTION_ATTR_CT] = (u32)-1, |
| 2232 | [OVS_ACTION_ATTR_TRUNC] = sizeof(struct ovs_action_trunc), | ||
| 2232 | }; | 2233 | }; |
| 2233 | const struct ovs_action_push_vlan *vlan; | 2234 | const struct ovs_action_push_vlan *vlan; |
| 2234 | int type = nla_type(a); | 2235 | int type = nla_type(a); |
| @@ -2255,6 +2256,14 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr, | |||
| 2255 | return -EINVAL; | 2256 | return -EINVAL; |
| 2256 | break; | 2257 | break; |
| 2257 | 2258 | ||
| 2259 | case OVS_ACTION_ATTR_TRUNC: { | ||
| 2260 | const struct ovs_action_trunc *trunc = nla_data(a); | ||
| 2261 | |||
| 2262 | if (trunc->max_len < ETH_HLEN) | ||
| 2263 | return -EINVAL; | ||
| 2264 | break; | ||
| 2265 | } | ||
| 2266 | |||
| 2258 | case OVS_ACTION_ATTR_HASH: { | 2267 | case OVS_ACTION_ATTR_HASH: { |
| 2259 | const struct ovs_action_hash *act_hash = nla_data(a); | 2268 | const struct ovs_action_hash *act_hash = nla_data(a); |
| 2260 | 2269 | ||
diff --git a/net/openvswitch/vport.c b/net/openvswitch/vport.c index 31cbc8c5c7db..6b21fd068d87 100644 --- a/net/openvswitch/vport.c +++ b/net/openvswitch/vport.c | |||
| @@ -444,6 +444,7 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb, | |||
| 444 | 444 | ||
| 445 | OVS_CB(skb)->input_vport = vport; | 445 | OVS_CB(skb)->input_vport = vport; |
| 446 | OVS_CB(skb)->mru = 0; | 446 | OVS_CB(skb)->mru = 0; |
| 447 | OVS_CB(skb)->cutlen = 0; | ||
| 447 | if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) { | 448 | if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) { |
| 448 | u32 mark; | 449 | u32 mark; |
| 449 | 450 | ||
