mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 16:20:55 +07:00
openvswitch: add processing of L3 packets
Support receiving, extracting flow key and sending of L3 packets (packets without an Ethernet header). Note that even after this patch, non-Ethernet interfaces are still not allowed to be added to bridges. Similarly, netlink interface for sending and receiving L3 packets to/from user space is not in place yet. Based on previous versions by Lorand Jakab and Simon Horman. Signed-off-by: Lorand Jakab <lojakab@cisco.com> Signed-off-by: Simon Horman <simon.horman@netronome.com> Signed-off-by: Jiri Benc <jbenc@redhat.com> Acked-by: Pravin B Shelar <pshelar@ovn.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
1560a074df
commit
5108bbaddc
@ -560,7 +560,6 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
|
|||||||
struct sw_flow *flow;
|
struct sw_flow *flow;
|
||||||
struct sw_flow_actions *sf_acts;
|
struct sw_flow_actions *sf_acts;
|
||||||
struct datapath *dp;
|
struct datapath *dp;
|
||||||
struct ethhdr *eth;
|
|
||||||
struct vport *input_vport;
|
struct vport *input_vport;
|
||||||
u16 mru = 0;
|
u16 mru = 0;
|
||||||
int len;
|
int len;
|
||||||
@ -581,17 +580,6 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
|
|||||||
|
|
||||||
nla_memcpy(__skb_put(packet, len), a[OVS_PACKET_ATTR_PACKET], len);
|
nla_memcpy(__skb_put(packet, len), a[OVS_PACKET_ATTR_PACKET], len);
|
||||||
|
|
||||||
skb_reset_mac_header(packet);
|
|
||||||
eth = eth_hdr(packet);
|
|
||||||
|
|
||||||
/* Normally, setting the skb 'protocol' field would be handled by a
|
|
||||||
* call to eth_type_trans(), but it assumes there's a sending
|
|
||||||
* device, which we may not have. */
|
|
||||||
if (eth_proto_is_802_3(eth->h_proto))
|
|
||||||
packet->protocol = eth->h_proto;
|
|
||||||
else
|
|
||||||
packet->protocol = htons(ETH_P_802_2);
|
|
||||||
|
|
||||||
/* Set packet's mru */
|
/* Set packet's mru */
|
||||||
if (a[OVS_PACKET_ATTR_MRU]) {
|
if (a[OVS_PACKET_ATTR_MRU]) {
|
||||||
mru = nla_get_u16(a[OVS_PACKET_ATTR_MRU]);
|
mru = nla_get_u16(a[OVS_PACKET_ATTR_MRU]);
|
||||||
@ -618,6 +606,7 @@ static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info)
|
|||||||
rcu_assign_pointer(flow->sf_acts, acts);
|
rcu_assign_pointer(flow->sf_acts, acts);
|
||||||
packet->priority = flow->key.phy.priority;
|
packet->priority = flow->key.phy.priority;
|
||||||
packet->mark = flow->key.phy.skb_mark;
|
packet->mark = flow->key.phy.skb_mark;
|
||||||
|
packet->protocol = flow->key.eth.type;
|
||||||
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
dp = get_dp_rcu(net, ovs_header->dp_ifindex);
|
dp = get_dp_rcu(net, ovs_header->dp_ifindex);
|
||||||
|
@ -334,14 +334,17 @@ static int parse_vlan_tag(struct sk_buff *skb, struct vlan_head *key_vh)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
|
static void clear_vlan(struct sw_flow_key *key)
|
||||||
{
|
{
|
||||||
int res;
|
|
||||||
|
|
||||||
key->eth.vlan.tci = 0;
|
key->eth.vlan.tci = 0;
|
||||||
key->eth.vlan.tpid = 0;
|
key->eth.vlan.tpid = 0;
|
||||||
key->eth.cvlan.tci = 0;
|
key->eth.cvlan.tci = 0;
|
||||||
key->eth.cvlan.tpid = 0;
|
key->eth.cvlan.tpid = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_vlan(struct sk_buff *skb, struct sw_flow_key *key)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
if (skb_vlan_tag_present(skb)) {
|
if (skb_vlan_tag_present(skb)) {
|
||||||
key->eth.vlan.tci = htons(skb->vlan_tci);
|
key->eth.vlan.tci = htons(skb->vlan_tci);
|
||||||
@ -483,17 +486,20 @@ static int parse_icmpv6(struct sk_buff *skb, struct sw_flow_key *key,
|
|||||||
*
|
*
|
||||||
* Returns 0 if successful, otherwise a negative errno value.
|
* Returns 0 if successful, otherwise a negative errno value.
|
||||||
*
|
*
|
||||||
* Initializes @skb header pointers as follows:
|
* Initializes @skb header fields as follows:
|
||||||
*
|
*
|
||||||
* - skb->mac_header: the Ethernet header.
|
* - skb->mac_header: the L2 header.
|
||||||
*
|
*
|
||||||
* - skb->network_header: just past the Ethernet header, or just past the
|
* - skb->network_header: just past the L2 header, or just past the
|
||||||
* VLAN header, to the first byte of the Ethernet payload.
|
* VLAN header, to the first byte of the L2 payload.
|
||||||
*
|
*
|
||||||
* - skb->transport_header: If key->eth.type is ETH_P_IP or ETH_P_IPV6
|
* - skb->transport_header: If key->eth.type is ETH_P_IP or ETH_P_IPV6
|
||||||
* on output, then just past the IP header, if one is present and
|
* on output, then just past the IP header, if one is present and
|
||||||
* of a correct length, otherwise the same as skb->network_header.
|
* of a correct length, otherwise the same as skb->network_header.
|
||||||
* For other key->eth.type values it is left untouched.
|
* For other key->eth.type values it is left untouched.
|
||||||
|
*
|
||||||
|
* - skb->protocol: the type of the data starting at skb->network_header.
|
||||||
|
* Equals to key->eth.type.
|
||||||
*/
|
*/
|
||||||
static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
|
static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
|
||||||
{
|
{
|
||||||
@ -505,28 +511,35 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key)
|
|||||||
|
|
||||||
skb_reset_mac_header(skb);
|
skb_reset_mac_header(skb);
|
||||||
|
|
||||||
/* Link layer. We are guaranteed to have at least the 14 byte Ethernet
|
/* Link layer. */
|
||||||
* header in the linear data area.
|
clear_vlan(key);
|
||||||
*/
|
if (key->mac_proto == MAC_PROTO_NONE) {
|
||||||
eth = eth_hdr(skb);
|
if (unlikely(eth_type_vlan(skb->protocol)))
|
||||||
ether_addr_copy(key->eth.src, eth->h_source);
|
return -EINVAL;
|
||||||
ether_addr_copy(key->eth.dst, eth->h_dest);
|
|
||||||
|
|
||||||
__skb_pull(skb, 2 * ETH_ALEN);
|
skb_reset_network_header(skb);
|
||||||
/* We are going to push all headers that we pull, so no need to
|
} else {
|
||||||
* update skb->csum here.
|
eth = eth_hdr(skb);
|
||||||
*/
|
ether_addr_copy(key->eth.src, eth->h_source);
|
||||||
|
ether_addr_copy(key->eth.dst, eth->h_dest);
|
||||||
|
|
||||||
if (unlikely(parse_vlan(skb, key)))
|
__skb_pull(skb, 2 * ETH_ALEN);
|
||||||
return -ENOMEM;
|
/* We are going to push all headers that we pull, so no need to
|
||||||
|
* update skb->csum here.
|
||||||
|
*/
|
||||||
|
|
||||||
key->eth.type = parse_ethertype(skb);
|
if (unlikely(parse_vlan(skb, key)))
|
||||||
if (unlikely(key->eth.type == htons(0)))
|
return -ENOMEM;
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
skb_reset_network_header(skb);
|
skb->protocol = parse_ethertype(skb);
|
||||||
|
if (unlikely(skb->protocol == htons(0)))
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
skb_reset_network_header(skb);
|
||||||
|
__skb_push(skb, skb->data - skb_mac_header(skb));
|
||||||
|
}
|
||||||
skb_reset_mac_len(skb);
|
skb_reset_mac_len(skb);
|
||||||
__skb_push(skb, skb->data - skb_mac_header(skb));
|
key->eth.type = skb->protocol;
|
||||||
|
|
||||||
/* Network layer. */
|
/* Network layer. */
|
||||||
if (key->eth.type == htons(ETH_P_IP)) {
|
if (key->eth.type == htons(ETH_P_IP)) {
|
||||||
@ -721,9 +734,25 @@ int ovs_flow_key_update(struct sk_buff *skb, struct sw_flow_key *key)
|
|||||||
return key_extract(skb, key);
|
return key_extract(skb, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int key_extract_mac_proto(struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
switch (skb->dev->type) {
|
||||||
|
case ARPHRD_ETHER:
|
||||||
|
return MAC_PROTO_ETHERNET;
|
||||||
|
case ARPHRD_NONE:
|
||||||
|
if (skb->protocol == htons(ETH_P_TEB))
|
||||||
|
return MAC_PROTO_ETHERNET;
|
||||||
|
return MAC_PROTO_NONE;
|
||||||
|
}
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
|
int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
|
||||||
struct sk_buff *skb, struct sw_flow_key *key)
|
struct sk_buff *skb, struct sw_flow_key *key)
|
||||||
{
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
/* Extract metadata from packet. */
|
/* Extract metadata from packet. */
|
||||||
if (tun_info) {
|
if (tun_info) {
|
||||||
key->tun_proto = ip_tunnel_info_af(tun_info);
|
key->tun_proto = ip_tunnel_info_af(tun_info);
|
||||||
@ -751,7 +780,10 @@ int ovs_flow_key_extract(const struct ip_tunnel_info *tun_info,
|
|||||||
key->phy.skb_mark = skb->mark;
|
key->phy.skb_mark = skb->mark;
|
||||||
ovs_ct_fill_key(skb, key);
|
ovs_ct_fill_key(skb, key);
|
||||||
key->ovs_flow_hash = 0;
|
key->ovs_flow_hash = 0;
|
||||||
key->mac_proto = MAC_PROTO_ETHERNET;
|
res = key_extract_mac_proto(skb);
|
||||||
|
if (res < 0)
|
||||||
|
return res;
|
||||||
|
key->mac_proto = res;
|
||||||
key->recirc_id = 0;
|
key->recirc_id = 0;
|
||||||
|
|
||||||
return key_extract(skb, key);
|
return key_extract(skb, key);
|
||||||
@ -768,5 +800,29 @@ int ovs_flow_key_extract_userspace(struct net *net, const struct nlattr *attr,
|
|||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
if (ovs_key_mac_proto(key) == MAC_PROTO_NONE) {
|
||||||
|
/* key_extract assumes that skb->protocol is set-up for
|
||||||
|
* layer 3 packets which is the case for other callers,
|
||||||
|
* in particular packets recieved from the network stack.
|
||||||
|
* Here the correct value can be set from the metadata
|
||||||
|
* extracted above.
|
||||||
|
*/
|
||||||
|
skb->protocol = key->eth.type;
|
||||||
|
} else {
|
||||||
|
struct ethhdr *eth;
|
||||||
|
|
||||||
|
skb_reset_mac_header(skb);
|
||||||
|
eth = eth_hdr(skb);
|
||||||
|
|
||||||
|
/* Normally, setting the skb 'protocol' field would be
|
||||||
|
* handled by a call to eth_type_trans(), but it assumes
|
||||||
|
* there's a sending device, which we may not have.
|
||||||
|
*/
|
||||||
|
if (eth_proto_is_802_3(eth->h_proto))
|
||||||
|
skb->protocol = eth->h_proto;
|
||||||
|
else
|
||||||
|
skb->protocol = htons(ETH_P_802_2);
|
||||||
|
}
|
||||||
|
|
||||||
return key_extract(skb, key);
|
return key_extract(skb, key);
|
||||||
}
|
}
|
||||||
|
@ -485,6 +485,25 @@ void ovs_vport_send(struct vport *vport, struct sk_buff *skb, u8 mac_proto)
|
|||||||
{
|
{
|
||||||
int mtu = vport->dev->mtu;
|
int mtu = vport->dev->mtu;
|
||||||
|
|
||||||
|
switch (vport->dev->type) {
|
||||||
|
case ARPHRD_NONE:
|
||||||
|
if (mac_proto == MAC_PROTO_ETHERNET) {
|
||||||
|
skb_reset_network_header(skb);
|
||||||
|
skb_reset_mac_len(skb);
|
||||||
|
skb->protocol = htons(ETH_P_TEB);
|
||||||
|
} else if (mac_proto != MAC_PROTO_NONE) {
|
||||||
|
WARN_ON_ONCE(1);
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case ARPHRD_ETHER:
|
||||||
|
if (mac_proto != MAC_PROTO_ETHERNET)
|
||||||
|
goto drop;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
|
||||||
if (unlikely(packet_length(skb, vport->dev) > mtu &&
|
if (unlikely(packet_length(skb, vport->dev) > mtu &&
|
||||||
!skb_is_gso(skb))) {
|
!skb_is_gso(skb))) {
|
||||||
net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n",
|
net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n",
|
||||||
|
Loading…
Reference in New Issue
Block a user