#include <net/lisp.h>
#include <net/gre.h>
#include <net/geneve.h>
-#include <net/vxlan.h>
#include <net/stt.h>
+#include <net/vxlan.h>
#include "datapath.h"
#include "gso.h"
#include "vport-internal_dev.h"
static LIST_HEAD(vport_ops_list);
+static bool compat_gre_loaded = false;
+static bool compat_ip6_tunnel_loaded = false;
/* Protected by RCU read lock for reading, ovs_mutex for writing. */
static struct hlist_head *dev_table;
{
int err;
- dev_table = kzalloc(VPORT_HASH_BUCKETS * sizeof(struct hlist_head),
+ dev_table = kcalloc(VPORT_HASH_BUCKETS, sizeof(struct hlist_head),
GFP_KERNEL);
if (!dev_table)
return -ENOMEM;
err = lisp_init_module();
if (err)
goto err_lisp;
- err = ipgre_init();
- if (err)
+ err = gre_init();
+ if (err && err != -EEXIST) {
goto err_gre;
+ } else {
+ if (err == -EEXIST) {
+ pr_warn("Cannot take GRE protocol rx entry"\
+ "- The GRE/ERSPAN rx feature not supported\n");
+ /* continue GRE tx */
+ }
+
+ err = ipgre_init();
+ if (err && err != -EEXIST)
+ goto err_ipgre;
+ compat_gre_loaded = true;
+ }
+ err = ip6gre_init();
+ if (err && err != -EEXIST) {
+ goto err_ip6gre;
+ } else {
+ if (err == -EEXIST) {
+ pr_warn("IPv6 GRE/ERSPAN Rx mode is not supported\n");
+ goto skip_ip6_tunnel_init;
+ }
+ }
+
+ err = ip6_tunnel_init();
+ if (err)
+ goto err_ip6_tunnel;
+ else
+ compat_ip6_tunnel_loaded = true;
+
+skip_ip6_tunnel_init:
err = geneve_init_module();
if (err)
goto err_geneve;
-
err = vxlan_init_module();
if (err)
goto err_vxlan;
err = ovs_stt_init_module();
if (err)
goto err_stt;
- return 0;
+ return 0;
+ ovs_stt_cleanup_module();
err_stt:
vxlan_cleanup_module();
err_vxlan:
geneve_cleanup_module();
err_geneve:
+ ip6_tunnel_cleanup();
+err_ip6_tunnel:
+ ip6gre_fini();
+err_ip6gre:
ipgre_fini();
+err_ipgre:
+ gre_exit();
err_gre:
lisp_cleanup_module();
err_lisp:
*/
void ovs_vport_exit(void)
{
+ if (compat_gre_loaded) {
+ gre_exit();
+ ipgre_fini();
+ }
ovs_stt_cleanup_module();
vxlan_cleanup_module();
geneve_cleanup_module();
- ipgre_fini();
+ if (compat_ip6_tunnel_loaded)
+ ip6_tunnel_cleanup();
+ ip6gre_fini();
lisp_cleanup_module();
kfree(dev_table);
}
return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)];
}
-int ovs_vport_ops_register(struct vport_ops *ops)
+int __ovs_vport_ops_register(struct vport_ops *ops)
{
int err = -EEXIST;
struct vport_ops *o;
ovs_unlock();
return err;
}
-EXPORT_SYMBOL_GPL(ovs_vport_ops_register);
+EXPORT_SYMBOL_GPL(__ovs_vport_ops_register);
void ovs_vport_ops_unregister(struct vport_ops *ops)
{
return vport;
}
+ if (parms->type == OVS_VPORT_TYPE_GRE && !compat_gre_loaded) {
+ pr_warn("GRE protocol already loaded!\n");
+ return ERR_PTR(-EAFNOSUPPORT);
+ }
/* Unlock to attempt module load and return -EAGAIN if load
* was successful as we need to restart the port addition
* workflow.
*
* @vport: vport to delete.
*
- * Detaches @vport from its datapath and destroys it. It is possible to fail
- * for reasons such as lack of memory. ovs_mutex must be held.
+ * Detaches @vport from its datapath and destroys it. ovs_mutex must be
+ * held.
*/
void ovs_vport_del(struct vport *vport)
{
if (!vport->ops->get_options)
return 0;
- nla = nla_nest_start(skb, OVS_VPORT_ATTR_OPTIONS);
+ nla = nla_nest_start_noflag(skb, OVS_VPORT_ATTR_OPTIONS);
if (!nla)
return -EMSGSIZE;
return 0;
}
-static void vport_portids_destroy_rcu_cb(struct rcu_head *rcu)
-{
- struct vport_portids *ids = container_of(rcu, struct vport_portids,
- rcu);
-
- kfree(ids);
-}
-
/**
* ovs_vport_set_upcall_portids - set upcall portids of @vport.
*
rcu_assign_pointer(vport->upcall_portids, vport_portids);
if (old)
- call_rcu(&old->rcu, vport_portids_destroy_rcu_cb);
+ kfree_rcu(old, rcu);
return 0;
}
ids = rcu_dereference(vport->upcall_portids);
- if (ids->n_ids == 1 && ids->ids[0] == 0)
- return 0;
+ /* If there is only one portid, select it in the fast-path. */
+ if (ids->n_ids == 1)
+ return ids->ids[0];
hash = skb_get_hash(skb);
ids_index = hash - ids->n_ids * reciprocal_divide(hash, ids->rn_ids);
OVS_CB(skb)->input_vport = vport;
OVS_CB(skb)->mru = 0;
+ OVS_CB(skb)->cutlen = 0;
+ if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) {
+ u32 mark;
+
+ mark = skb->mark;
+ skb_scrub_packet(skb, true);
+ skb->mark = mark;
+ tun_info = NULL;
+ }
+
ovs_skb_init_inner_protocol(skb);
skb_clear_ovs_gso_cb(skb);
/* Extract flow from 'skb' into 'key'. */
ovs_dp_process_packet(skb, &key);
return 0;
}
-EXPORT_SYMBOL_GPL(ovs_vport_receive);
-
-static void free_vport_rcu(struct rcu_head *rcu)
-{
- struct vport *vport = container_of(rcu, struct vport, rcu);
-
- ovs_vport_free(vport);
-}
-void ovs_vport_deferred_free(struct vport *vport)
-{
- if (!vport)
- return;
-
- call_rcu(&vport->rcu, free_vport_rcu);
-}
-EXPORT_SYMBOL_GPL(ovs_vport_deferred_free);
-
-int ovs_tunnel_get_egress_info(struct dp_upcall_info *upcall,
- struct net *net,
- struct sk_buff *skb,
- u8 ipproto,
- __be16 tp_src,
- __be16 tp_dst)
+static int packet_length(const struct sk_buff *skb,
+ struct net_device *dev)
{
- struct ip_tunnel_info *egress_tun_info = upcall->egress_tun_info;
- struct ip_tunnel_info *tun_info = skb_tunnel_info(skb);
- const struct ip_tunnel_key *tun_key;
- u32 skb_mark = skb->mark;
- struct rtable *rt;
- struct flowi4 fl;
-
- if (unlikely(!tun_info))
- return -EINVAL;
- if (ip_tunnel_info_af(tun_info) != AF_INET)
- return -EINVAL;
+ int length = skb->len - dev->hard_header_len;
- tun_key = &tun_info->key;
-
- /* Route lookup to get srouce IP address.
- * The process may need to be changed if the corresponding process
- * in vports ops changed.
- */
- rt = ovs_tunnel_route_lookup(net, tun_key, skb_mark, &fl, ipproto);
- if (IS_ERR(rt))
- return PTR_ERR(rt);
-
- ip_rt_put(rt);
+ if (!skb_vlan_tag_present(skb) &&
+ eth_type_vlan(skb->protocol))
+ length -= VLAN_HLEN;
- /* Generate egress_tun_info based on tun_info,
- * saddr, tp_src and tp_dst
+ /* Don't subtract for multiple VLAN tags. Most (all?) drivers allow
+ * (ETH_LEN + VLAN_HLEN) in addition to the mtu value, but almost none
+ * account for 802.1ad. e.g. is_skb_forwardable().
*/
- ip_tunnel_key_init(&egress_tun_info->key,
- fl.saddr, tun_key->u.ipv4.dst,
- tun_key->tos,
- tun_key->ttl,
- tp_src, tp_dst,
- tun_key->tun_id,
- tun_key->tun_flags);
- egress_tun_info->options_len = tun_info->options_len;
- egress_tun_info->mode = tun_info->mode;
- upcall->egress_tun_opts = ip_tunnel_info_opts(tun_info);
- return 0;
-}
-EXPORT_SYMBOL_GPL(ovs_tunnel_get_egress_info);
-
-int ovs_vport_get_egress_tun_info(struct vport *vport, struct sk_buff *skb,
- struct dp_upcall_info *upcall)
-{
- /* get_egress_tun_info() is only implemented on tunnel ports. */
- if (unlikely(!vport->ops->get_egress_tun_info))
- return -EINVAL;
-
- return vport->ops->get_egress_tun_info(vport, skb, upcall);
-}
-
-static unsigned int packet_length(const struct sk_buff *skb)
-{
- unsigned int length = skb->len - ETH_HLEN;
-
- if (skb->protocol == htons(ETH_P_8021Q))
- length -= VLAN_HLEN;
- return length;
+ return length > 0 ? length: 0;
}
-void ovs_vport_send(struct vport *vport, struct sk_buff *skb)
+void ovs_vport_send(struct vport *vport, struct sk_buff *skb, u8 mac_proto)
{
int mtu = vport->dev->mtu;
- if (unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) {
+ 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 &&
+ !skb_is_gso(skb))) {
net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n",
vport->dev->name,
- packet_length(skb), mtu);
+ packet_length(skb, vport->dev), mtu);
vport->dev->stats.tx_errors++;
goto drop;
}