mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-15 08:14:15 +08:00
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>
This commit is contained in:
parent
1578b0a5e9
commit
f2a4d086ed
@ -580,6 +580,10 @@ enum ovs_userspace_attr {
|
|||||||
|
|
||||||
#define OVS_USERSPACE_ATTR_MAX (__OVS_USERSPACE_ATTR_MAX - 1)
|
#define OVS_USERSPACE_ATTR_MAX (__OVS_USERSPACE_ATTR_MAX - 1)
|
||||||
|
|
||||||
|
struct ovs_action_trunc {
|
||||||
|
uint32_t max_len; /* Max packet size in bytes. */
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct ovs_action_push_mpls - %OVS_ACTION_ATTR_PUSH_MPLS action argument.
|
* struct ovs_action_push_mpls - %OVS_ACTION_ATTR_PUSH_MPLS action argument.
|
||||||
* @mpls_lse: MPLS label stack entry to push.
|
* @mpls_lse: MPLS label stack entry to push.
|
||||||
@ -703,6 +707,7 @@ enum ovs_nat_attr {
|
|||||||
* enum ovs_action_attr - Action types.
|
* enum ovs_action_attr - Action types.
|
||||||
*
|
*
|
||||||
* @OVS_ACTION_ATTR_OUTPUT: Output packet to port.
|
* @OVS_ACTION_ATTR_OUTPUT: Output packet to port.
|
||||||
|
* @OVS_ACTION_ATTR_TRUNC: Output packet to port with truncated packet size.
|
||||||
* @OVS_ACTION_ATTR_USERSPACE: Send packet to userspace according to nested
|
* @OVS_ACTION_ATTR_USERSPACE: Send packet to userspace according to nested
|
||||||
* %OVS_USERSPACE_ATTR_* attributes.
|
* %OVS_USERSPACE_ATTR_* attributes.
|
||||||
* @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header. The
|
* @OVS_ACTION_ATTR_SET: Replaces the contents of an existing header. The
|
||||||
@ -756,6 +761,7 @@ enum ovs_action_attr {
|
|||||||
* The data must be zero for the unmasked
|
* The data must be zero for the unmasked
|
||||||
* bits. */
|
* bits. */
|
||||||
OVS_ACTION_ATTR_CT, /* Nested OVS_CT_ATTR_* . */
|
OVS_ACTION_ATTR_CT, /* Nested OVS_CT_ATTR_* . */
|
||||||
|
OVS_ACTION_ATTR_TRUNC, /* u32 struct ovs_action_trunc. */
|
||||||
|
|
||||||
__OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted
|
__OVS_ACTION_ATTR_MAX, /* Nothing past this will be accepted
|
||||||
* from userspace. */
|
* from userspace. */
|
||||||
|
@ -750,6 +750,14 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port,
|
|||||||
|
|
||||||
if (likely(vport)) {
|
if (likely(vport)) {
|
||||||
u16 mru = OVS_CB(skb)->mru;
|
u16 mru = OVS_CB(skb)->mru;
|
||||||
|
u32 cutlen = OVS_CB(skb)->cutlen;
|
||||||
|
|
||||||
|
if (unlikely(cutlen > 0)) {
|
||||||
|
if (skb->len - cutlen > ETH_HLEN)
|
||||||
|
pskb_trim(skb, skb->len - cutlen);
|
||||||
|
else
|
||||||
|
pskb_trim(skb, ETH_HLEN);
|
||||||
|
}
|
||||||
|
|
||||||
if (likely(!mru || (skb->len <= mru + ETH_HLEN))) {
|
if (likely(!mru || (skb->len <= mru + ETH_HLEN))) {
|
||||||
ovs_vport_send(vport, skb);
|
ovs_vport_send(vport, skb);
|
||||||
@ -775,7 +783,8 @@ static void do_output(struct datapath *dp, struct sk_buff *skb, int out_port,
|
|||||||
|
|
||||||
static int output_userspace(struct datapath *dp, struct sk_buff *skb,
|
static int output_userspace(struct datapath *dp, struct sk_buff *skb,
|
||||||
struct sw_flow_key *key, const struct nlattr *attr,
|
struct sw_flow_key *key, const struct nlattr *attr,
|
||||||
const struct nlattr *actions, int actions_len)
|
const struct nlattr *actions, int actions_len,
|
||||||
|
uint32_t cutlen)
|
||||||
{
|
{
|
||||||
struct dp_upcall_info upcall;
|
struct dp_upcall_info upcall;
|
||||||
const struct nlattr *a;
|
const struct nlattr *a;
|
||||||
@ -822,7 +831,7 @@ static int output_userspace(struct datapath *dp, struct sk_buff *skb,
|
|||||||
} /* End of switch. */
|
} /* End of switch. */
|
||||||
}
|
}
|
||||||
|
|
||||||
return ovs_dp_upcall(dp, skb, key, &upcall);
|
return ovs_dp_upcall(dp, skb, key, &upcall, cutlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sample(struct datapath *dp, struct sk_buff *skb,
|
static int sample(struct datapath *dp, struct sk_buff *skb,
|
||||||
@ -832,6 +841,7 @@ static int sample(struct datapath *dp, struct sk_buff *skb,
|
|||||||
const struct nlattr *acts_list = NULL;
|
const struct nlattr *acts_list = NULL;
|
||||||
const struct nlattr *a;
|
const struct nlattr *a;
|
||||||
int rem;
|
int rem;
|
||||||
|
u32 cutlen = 0;
|
||||||
|
|
||||||
for (a = nla_data(attr), rem = nla_len(attr); rem > 0;
|
for (a = nla_data(attr), rem = nla_len(attr); rem > 0;
|
||||||
a = nla_next(a, &rem)) {
|
a = nla_next(a, &rem)) {
|
||||||
@ -858,13 +868,24 @@ static int sample(struct datapath *dp, struct sk_buff *skb,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* The only known usage of sample action is having a single user-space
|
/* The only known usage of sample action is having a single user-space
|
||||||
|
* action, or having a truncate action followed by a single user-space
|
||||||
* action. Treat this usage as a special case.
|
* action. Treat this usage as a special case.
|
||||||
* The output_userspace() should clone the skb to be sent to the
|
* The output_userspace() should clone the skb to be sent to the
|
||||||
* user space. This skb will be consumed by its caller.
|
* user space. This skb will be consumed by its caller.
|
||||||
*/
|
*/
|
||||||
|
if (unlikely(nla_type(a) == OVS_ACTION_ATTR_TRUNC)) {
|
||||||
|
struct ovs_action_trunc *trunc = nla_data(a);
|
||||||
|
|
||||||
|
if (skb->len > trunc->max_len)
|
||||||
|
cutlen = skb->len - trunc->max_len;
|
||||||
|
|
||||||
|
a = nla_next(a, &rem);
|
||||||
|
}
|
||||||
|
|
||||||
if (likely(nla_type(a) == OVS_ACTION_ATTR_USERSPACE &&
|
if (likely(nla_type(a) == OVS_ACTION_ATTR_USERSPACE &&
|
||||||
nla_is_last(a, rem)))
|
nla_is_last(a, rem)))
|
||||||
return output_userspace(dp, skb, key, a, actions, actions_len);
|
return output_userspace(dp, skb, key, a, actions,
|
||||||
|
actions_len, cutlen);
|
||||||
|
|
||||||
skb = skb_clone(skb, GFP_ATOMIC);
|
skb = skb_clone(skb, GFP_ATOMIC);
|
||||||
if (!skb)
|
if (!skb)
|
||||||
@ -1051,6 +1072,7 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
|
|||||||
if (out_skb)
|
if (out_skb)
|
||||||
do_output(dp, out_skb, prev_port, key);
|
do_output(dp, out_skb, prev_port, key);
|
||||||
|
|
||||||
|
OVS_CB(skb)->cutlen = 0;
|
||||||
prev_port = -1;
|
prev_port = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1059,8 +1081,18 @@ static int do_execute_actions(struct datapath *dp, struct sk_buff *skb,
|
|||||||
prev_port = nla_get_u32(a);
|
prev_port = nla_get_u32(a);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OVS_ACTION_ATTR_TRUNC: {
|
||||||
|
struct ovs_action_trunc *trunc = nla_data(a);
|
||||||
|
|
||||||
|
if (skb->len > trunc->max_len)
|
||||||
|
OVS_CB(skb)->cutlen = skb->len - trunc->max_len;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case OVS_ACTION_ATTR_USERSPACE:
|
case OVS_ACTION_ATTR_USERSPACE:
|
||||||
output_userspace(dp, skb, key, a, attr, len);
|
output_userspace(dp, skb, key, a, attr,
|
||||||
|
len, OVS_CB(skb)->cutlen);
|
||||||
|
OVS_CB(skb)->cutlen = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OVS_ACTION_ATTR_HASH:
|
case OVS_ACTION_ATTR_HASH:
|
||||||
|
@ -137,10 +137,12 @@ EXPORT_SYMBOL_GPL(lockdep_ovsl_is_held);
|
|||||||
static struct vport *new_vport(const struct vport_parms *);
|
static struct vport *new_vport(const struct vport_parms *);
|
||||||
static int queue_gso_packets(struct datapath *dp, struct sk_buff *,
|
static int queue_gso_packets(struct datapath *dp, struct sk_buff *,
|
||||||
const struct sw_flow_key *,
|
const struct sw_flow_key *,
|
||||||
const struct dp_upcall_info *);
|
const struct dp_upcall_info *,
|
||||||
|
uint32_t cutlen);
|
||||||
static int queue_userspace_packet(struct datapath *dp, struct sk_buff *,
|
static int queue_userspace_packet(struct datapath *dp, struct sk_buff *,
|
||||||
const struct sw_flow_key *,
|
const struct sw_flow_key *,
|
||||||
const struct dp_upcall_info *);
|
const struct dp_upcall_info *,
|
||||||
|
uint32_t cutlen);
|
||||||
|
|
||||||
/* Must be called with rcu_read_lock. */
|
/* Must be called with rcu_read_lock. */
|
||||||
static struct datapath *get_dp_rcu(struct net *net, int dp_ifindex)
|
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)
|
|||||||
upcall.cmd = OVS_PACKET_CMD_MISS;
|
upcall.cmd = OVS_PACKET_CMD_MISS;
|
||||||
upcall.portid = ovs_vport_find_upcall_portid(p, skb);
|
upcall.portid = ovs_vport_find_upcall_portid(p, skb);
|
||||||
upcall.mru = OVS_CB(skb)->mru;
|
upcall.mru = OVS_CB(skb)->mru;
|
||||||
error = ovs_dp_upcall(dp, skb, key, &upcall);
|
error = ovs_dp_upcall(dp, skb, key, &upcall, 0);
|
||||||
if (unlikely(error))
|
if (unlikely(error))
|
||||||
kfree_skb(skb);
|
kfree_skb(skb);
|
||||||
else
|
else
|
||||||
@ -300,7 +302,8 @@ out:
|
|||||||
|
|
||||||
int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
|
int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
|
||||||
const struct sw_flow_key *key,
|
const struct sw_flow_key *key,
|
||||||
const struct dp_upcall_info *upcall_info)
|
const struct dp_upcall_info *upcall_info,
|
||||||
|
uint32_t cutlen)
|
||||||
{
|
{
|
||||||
struct dp_stats_percpu *stats;
|
struct dp_stats_percpu *stats;
|
||||||
int err;
|
int err;
|
||||||
@ -311,9 +314,9 @@ int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!skb_is_gso(skb))
|
if (!skb_is_gso(skb))
|
||||||
err = queue_userspace_packet(dp, skb, key, upcall_info);
|
err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen);
|
||||||
else
|
else
|
||||||
err = queue_gso_packets(dp, skb, key, upcall_info);
|
err = queue_gso_packets(dp, skb, key, upcall_info, cutlen);
|
||||||
if (err)
|
if (err)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@ -331,7 +334,8 @@ err:
|
|||||||
|
|
||||||
static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
|
static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
|
||||||
const struct sw_flow_key *key,
|
const struct sw_flow_key *key,
|
||||||
const struct dp_upcall_info *upcall_info)
|
const struct dp_upcall_info *upcall_info,
|
||||||
|
uint32_t cutlen)
|
||||||
{
|
{
|
||||||
unsigned short gso_type = skb_shinfo(skb)->gso_type;
|
unsigned short gso_type = skb_shinfo(skb)->gso_type;
|
||||||
struct sw_flow_key later_key;
|
struct sw_flow_key later_key;
|
||||||
@ -360,7 +364,7 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb,
|
|||||||
if (gso_type & SKB_GSO_UDP && skb != segs)
|
if (gso_type & SKB_GSO_UDP && skb != segs)
|
||||||
key = &later_key;
|
key = &later_key;
|
||||||
|
|
||||||
err = queue_userspace_packet(dp, skb, key, upcall_info);
|
err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen);
|
||||||
if (err)
|
if (err)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -416,7 +420,8 @@ static void pad_packet(struct datapath *dp, struct sk_buff *skb)
|
|||||||
|
|
||||||
static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
|
static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
|
||||||
const struct sw_flow_key *key,
|
const struct sw_flow_key *key,
|
||||||
const struct dp_upcall_info *upcall_info)
|
const struct dp_upcall_info *upcall_info,
|
||||||
|
uint32_t cutlen)
|
||||||
{
|
{
|
||||||
struct ovs_header *upcall;
|
struct ovs_header *upcall;
|
||||||
struct sk_buff *nskb = NULL;
|
struct sk_buff *nskb = NULL;
|
||||||
@ -461,7 +466,7 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
|
|||||||
else
|
else
|
||||||
hlen = skb->len;
|
hlen = skb->len;
|
||||||
|
|
||||||
len = upcall_msg_size(upcall_info, hlen);
|
len = upcall_msg_size(upcall_info, hlen - cutlen);
|
||||||
user_skb = genlmsg_new(len, GFP_ATOMIC);
|
user_skb = genlmsg_new(len, GFP_ATOMIC);
|
||||||
if (!user_skb) {
|
if (!user_skb) {
|
||||||
err = -ENOMEM;
|
err = -ENOMEM;
|
||||||
@ -515,9 +520,9 @@ static int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
|
|||||||
err = -ENOBUFS;
|
err = -ENOBUFS;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
nla->nla_len = nla_attr_size(skb->len);
|
nla->nla_len = nla_attr_size(skb->len - cutlen);
|
||||||
|
|
||||||
err = skb_zerocopy(user_skb, skb, skb->len, hlen);
|
err = skb_zerocopy(user_skb, skb, skb->len - cutlen, hlen);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
@ -100,11 +100,13 @@ struct datapath {
|
|||||||
* @input_vport: The original vport packet came in on. This value is cached
|
* @input_vport: The original vport packet came in on. This value is cached
|
||||||
* when a packet is received by OVS.
|
* when a packet is received by OVS.
|
||||||
* @mru: The maximum received fragement size; 0 if the packet is not
|
* @mru: The maximum received fragement size; 0 if the packet is not
|
||||||
|
* @cutlen: The number of bytes from the packet end to be removed.
|
||||||
* fragmented.
|
* fragmented.
|
||||||
*/
|
*/
|
||||||
struct ovs_skb_cb {
|
struct ovs_skb_cb {
|
||||||
struct vport *input_vport;
|
struct vport *input_vport;
|
||||||
u16 mru;
|
u16 mru;
|
||||||
|
u32 cutlen;
|
||||||
};
|
};
|
||||||
#define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)
|
#define OVS_CB(skb) ((struct ovs_skb_cb *)(skb)->cb)
|
||||||
|
|
||||||
@ -194,7 +196,8 @@ extern struct genl_family dp_vport_genl_family;
|
|||||||
void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key);
|
void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key);
|
||||||
void ovs_dp_detach_port(struct vport *);
|
void ovs_dp_detach_port(struct vport *);
|
||||||
int ovs_dp_upcall(struct datapath *, struct sk_buff *,
|
int ovs_dp_upcall(struct datapath *, struct sk_buff *,
|
||||||
const struct sw_flow_key *, const struct dp_upcall_info *);
|
const struct sw_flow_key *, const struct dp_upcall_info *,
|
||||||
|
uint32_t cutlen);
|
||||||
|
|
||||||
const char *ovs_dp_name(const struct datapath *dp);
|
const char *ovs_dp_name(const struct datapath *dp);
|
||||||
struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq,
|
struct sk_buff *ovs_vport_cmd_build_info(struct vport *, u32 pid, u32 seq,
|
||||||
|
@ -2229,6 +2229,7 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
|
|||||||
[OVS_ACTION_ATTR_SAMPLE] = (u32)-1,
|
[OVS_ACTION_ATTR_SAMPLE] = (u32)-1,
|
||||||
[OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash),
|
[OVS_ACTION_ATTR_HASH] = sizeof(struct ovs_action_hash),
|
||||||
[OVS_ACTION_ATTR_CT] = (u32)-1,
|
[OVS_ACTION_ATTR_CT] = (u32)-1,
|
||||||
|
[OVS_ACTION_ATTR_TRUNC] = sizeof(struct ovs_action_trunc),
|
||||||
};
|
};
|
||||||
const struct ovs_action_push_vlan *vlan;
|
const struct ovs_action_push_vlan *vlan;
|
||||||
int type = nla_type(a);
|
int type = nla_type(a);
|
||||||
@ -2255,6 +2256,14 @@ static int __ovs_nla_copy_actions(struct net *net, const struct nlattr *attr,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OVS_ACTION_ATTR_TRUNC: {
|
||||||
|
const struct ovs_action_trunc *trunc = nla_data(a);
|
||||||
|
|
||||||
|
if (trunc->max_len < ETH_HLEN)
|
||||||
|
return -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case OVS_ACTION_ATTR_HASH: {
|
case OVS_ACTION_ATTR_HASH: {
|
||||||
const struct ovs_action_hash *act_hash = nla_data(a);
|
const struct ovs_action_hash *act_hash = nla_data(a);
|
||||||
|
|
||||||
|
@ -444,6 +444,7 @@ int ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
|
|||||||
|
|
||||||
OVS_CB(skb)->input_vport = vport;
|
OVS_CB(skb)->input_vport = vport;
|
||||||
OVS_CB(skb)->mru = 0;
|
OVS_CB(skb)->mru = 0;
|
||||||
|
OVS_CB(skb)->cutlen = 0;
|
||||||
if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) {
|
if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) {
|
||||||
u32 mark;
|
u32 mark;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user