net: ip_gre: Separate ERSPAN newlink / changelink callbacks
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: 84e54fe0a5 ("gre: introduce native tunnel support for ERSPAN")
Signed-off-by: Petr Machata <petrm@mellanox.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
			
			
This commit is contained in:
		
							parent
							
								
									46ea929b2b
								
							
						
					
					
						commit
						e1f8f78ffe
					
				| @ -1153,6 +1153,22 @@ static int ipgre_netlink_parms(struct net_device *dev, | |||||||
| 	if (data[IFLA_GRE_FWMARK]) | 	if (data[IFLA_GRE_FWMARK]) | ||||||
| 		*fwmark = nla_get_u32(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]) { | 	if (data[IFLA_GRE_ERSPAN_VER]) { | ||||||
| 		t->erspan_ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]); | 		t->erspan_ver = nla_get_u8(data[IFLA_GRE_ERSPAN_VER]); | ||||||
| 
 | 
 | ||||||
| @ -1276,45 +1292,70 @@ static void ipgre_tap_setup(struct net_device *dev) | |||||||
| 	ip_tunnel_setup(dev, gre_tap_net_id); | 	ip_tunnel_setup(dev, gre_tap_net_id); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static int ipgre_newlink(struct net *src_net, struct net_device *dev, | static int | ||||||
| 			 struct nlattr *tb[], struct nlattr *data[], | ipgre_newlink_encap_setup(struct net_device *dev, struct nlattr *data[]) | ||||||
| 			 struct netlink_ext_ack *extack) |  | ||||||
| { | { | ||||||
| 	struct ip_tunnel_parm p; |  | ||||||
| 	struct ip_tunnel_encap ipencap; | 	struct ip_tunnel_encap ipencap; | ||||||
| 	__u32 fwmark = 0; |  | ||||||
| 	int err; |  | ||||||
| 
 | 
 | ||||||
| 	if (ipgre_netlink_encap_parms(data, &ipencap)) { | 	if (ipgre_netlink_encap_parms(data, &ipencap)) { | ||||||
| 		struct ip_tunnel *t = netdev_priv(dev); | 		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) | 		if (err < 0) | ||||||
| 			return err; | 			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); | 	err = ipgre_netlink_parms(dev, data, tb, &p, &fwmark); | ||||||
| 	if (err < 0) | 	if (err < 0) | ||||||
| 		return err; | 		return err; | ||||||
| 	return ip_tunnel_newlink(dev, tb, &p, fwmark); | 	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[], | static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[], | ||||||
| 			    struct nlattr *data[], | 			    struct nlattr *data[], | ||||||
| 			    struct netlink_ext_ack *extack) | 			    struct netlink_ext_ack *extack) | ||||||
| { | { | ||||||
| 	struct ip_tunnel *t = netdev_priv(dev); | 	struct ip_tunnel *t = netdev_priv(dev); | ||||||
| 	struct ip_tunnel_encap ipencap; |  | ||||||
| 	__u32 fwmark = t->fwmark; | 	__u32 fwmark = t->fwmark; | ||||||
| 	struct ip_tunnel_parm p; | 	struct ip_tunnel_parm p; | ||||||
| 	int err; | 	int err; | ||||||
| 
 | 
 | ||||||
| 	if (ipgre_netlink_encap_parms(data, &ipencap)) { | 	err = ipgre_newlink_encap_setup(dev, data); | ||||||
| 		err = ip_tunnel_encap_setup(t, &ipencap); | 	if (err) | ||||||
| 
 |  | ||||||
| 		if (err < 0) |  | ||||||
| 		return err; | 		return err; | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	err = ipgre_netlink_parms(dev, data, tb, &p, &fwmark); | 	err = ipgre_netlink_parms(dev, data, tb, &p, &fwmark); | ||||||
| 	if (err < 0) | 	if (err < 0) | ||||||
| @ -1327,12 +1368,38 @@ static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[], | |||||||
| 	t->parms.i_flags = p.i_flags; | 	t->parms.i_flags = p.i_flags; | ||||||
| 	t->parms.o_flags = p.o_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; | 	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; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| static size_t ipgre_get_size(const struct net_device *dev) | static size_t ipgre_get_size(const struct net_device *dev) | ||||||
| { | { | ||||||
| 	return | 	return | ||||||
| @ -1519,8 +1586,8 @@ static struct rtnl_link_ops erspan_link_ops __read_mostly = { | |||||||
| 	.priv_size	= sizeof(struct ip_tunnel), | 	.priv_size	= sizeof(struct ip_tunnel), | ||||||
| 	.setup		= erspan_setup, | 	.setup		= erspan_setup, | ||||||
| 	.validate	= erspan_validate, | 	.validate	= erspan_validate, | ||||||
| 	.newlink	= ipgre_newlink, | 	.newlink	= erspan_newlink, | ||||||
| 	.changelink	= ipgre_changelink, | 	.changelink	= erspan_changelink, | ||||||
| 	.dellink	= ip_tunnel_dellink, | 	.dellink	= ip_tunnel_dellink, | ||||||
| 	.get_size	= ipgre_get_size, | 	.get_size	= ipgre_get_size, | ||||||
| 	.fill_info	= ipgre_fill_info, | 	.fill_info	= ipgre_fill_info, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user