Skip to content

Commit

Permalink
net: ip_gre: Separate ERSPAN newlink / changelink callbacks
Browse files Browse the repository at this point in the history
ERSPAN shares most of the code path with GRE and gretap code. While that
helps keep the code compact, it is also error prone. Currently a broken
userspace can turn a gretap tunnel into a de facto ERSPAN one by passing
IFLA_GRE_ERSPAN_VER. There has been a similar issue in ip6gretap in the
past.

To prevent these problems in future, split the newlink and changelink code
paths. Split the ERSPAN code out of ipgre_netlink_parms() into a new
function erspan_netlink_parms(). Extract a piece of common logic from
ipgre_newlink() and ipgre_changelink() into ipgre_newlink_encap_setup().
Add erspan_newlink() and erspan_changelink().

Fixes: 84e54fe ("gre: introduce native tunnel support for ERSPAN")
Signed-off-by: Petr Machata <petrm@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Petr Machata authored and David S. Miller committed Mar 15, 2020
1 parent 46ea929 commit e1f8f78
Showing 1 changed file with 85 additions and 18 deletions.
103 changes: 85 additions & 18 deletions net/ipv4/ip_gre.c
Original file line number Diff line number Diff line change
Expand Up @@ -1153,6 +1153,22 @@ static int ipgre_netlink_parms(struct net_device *dev,
if (data[IFLA_GRE_FWMARK])
*fwmark = nla_get_u32(data[IFLA_GRE_FWMARK]);

return 0;
}

static int erspan_netlink_parms(struct net_device *dev,
struct nlattr *data[],
struct nlattr *tb[],
struct ip_tunnel_parm *parms,
__u32 *fwmark)
{
struct ip_tunnel *t = netdev_priv(dev);
int err;

err = ipgre_netlink_parms(dev, data, tb, parms, fwmark);
if (err)
return err;

if (data[IFLA_GRE_ERSPAN_VER]) {
t->erspan_ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]);

Expand Down Expand Up @@ -1276,45 +1292,70 @@ static void ipgre_tap_setup(struct net_device *dev)
ip_tunnel_setup(dev, gre_tap_net_id);
}

static int ipgre_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
static int
ipgre_newlink_encap_setup(struct net_device *dev, struct nlattr *data[])
{
struct ip_tunnel_parm p;
struct ip_tunnel_encap ipencap;
__u32 fwmark = 0;
int err;

if (ipgre_netlink_encap_parms(data, &ipencap)) {
struct ip_tunnel *t = netdev_priv(dev);
err = ip_tunnel_encap_setup(t, &ipencap);
int err = ip_tunnel_encap_setup(t, &ipencap);

if (err < 0)
return err;
}

return 0;
}

static int ipgre_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
struct ip_tunnel_parm p;
__u32 fwmark = 0;
int err;

err = ipgre_newlink_encap_setup(dev, data);
if (err)
return err;

err = ipgre_netlink_parms(dev, data, tb, &p, &fwmark);
if (err < 0)
return err;
return ip_tunnel_newlink(dev, tb, &p, fwmark);
}

static int erspan_newlink(struct net *src_net, struct net_device *dev,
struct nlattr *tb[], struct nlattr *data[],
struct netlink_ext_ack *extack)
{
struct ip_tunnel_parm p;
__u32 fwmark = 0;
int err;

err = ipgre_newlink_encap_setup(dev, data);
if (err)
return err;

err = erspan_netlink_parms(dev, data, tb, &p, &fwmark);
if (err)
return err;
return ip_tunnel_newlink(dev, tb, &p, fwmark);
}

static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[],
struct netlink_ext_ack *extack)
{
struct ip_tunnel *t = netdev_priv(dev);
struct ip_tunnel_encap ipencap;
__u32 fwmark = t->fwmark;
struct ip_tunnel_parm p;
int err;

if (ipgre_netlink_encap_parms(data, &ipencap)) {
err = ip_tunnel_encap_setup(t, &ipencap);

if (err < 0)
return err;
}
err = ipgre_newlink_encap_setup(dev, data);
if (err)
return err;

err = ipgre_netlink_parms(dev, data, tb, &p, &fwmark);
if (err < 0)
Expand All @@ -1327,8 +1368,34 @@ static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[],
t->parms.i_flags = p.i_flags;
t->parms.o_flags = p.o_flags;

if (strcmp(dev->rtnl_link_ops->kind, "erspan"))
ipgre_link_update(dev, !tb[IFLA_MTU]);
ipgre_link_update(dev, !tb[IFLA_MTU]);

return 0;
}

static int erspan_changelink(struct net_device *dev, struct nlattr *tb[],
struct nlattr *data[],
struct netlink_ext_ack *extack)
{
struct ip_tunnel *t = netdev_priv(dev);
__u32 fwmark = t->fwmark;
struct ip_tunnel_parm p;
int err;

err = ipgre_newlink_encap_setup(dev, data);
if (err)
return err;

err = erspan_netlink_parms(dev, data, tb, &p, &fwmark);
if (err < 0)
return err;

err = ip_tunnel_changelink(dev, tb, &p, fwmark);
if (err < 0)
return err;

t->parms.i_flags = p.i_flags;
t->parms.o_flags = p.o_flags;

return 0;
}
Expand Down Expand Up @@ -1519,8 +1586,8 @@ static struct rtnl_link_ops erspan_link_ops __read_mostly = {
.priv_size = sizeof(struct ip_tunnel),
.setup = erspan_setup,
.validate = erspan_validate,
.newlink = ipgre_newlink,
.changelink = ipgre_changelink,
.newlink = erspan_newlink,
.changelink = erspan_changelink,
.dellink = ip_tunnel_dellink,
.get_size = ipgre_get_size,
.fill_info = ipgre_fill_info,
Expand Down

0 comments on commit e1f8f78

Please sign in to comment.