diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2008-10-09 15:00:17 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-10-09 15:00:17 -0400 |
commit | e1a8000228e16212c93b23cfbed4d622e2ec7a6b (patch) | |
tree | 8a2b26a105abea23778228e7ea6d95500037a632 /net/ipv4/ip_gre.c | |
parent | c19e654ddbe3831252f61e76a74d661e1a755530 (diff) |
gre: Add Transparent Ethernet Bridging
This patch adds support for Ethernet over GRE encapsulation.
This is exposed to user-space with a new link type of "gretap"
instead of "gre". It will create an ARPHRD_ETHER device in
lieu of the usual ARPHRD_IPGRE.
Note that to preserver backwards compatibility all Transparent
Ethernet Bridging packets are passed to an ARPHRD_IPGRE tunnel
if its key matches and there is no ARPHRD_ETHER device whose
key matches more closely.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv4/ip_gre.c')
-rw-r--r-- | net/ipv4/ip_gre.c | 206 |
1 files changed, 174 insertions, 32 deletions
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 25d2c77a7f38..44ed9487fa15 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include <linux/inetdevice.h> | 27 | #include <linux/inetdevice.h> |
28 | #include <linux/igmp.h> | 28 | #include <linux/igmp.h> |
29 | #include <linux/netfilter_ipv4.h> | 29 | #include <linux/netfilter_ipv4.h> |
30 | #include <linux/etherdevice.h> | ||
30 | #include <linux/if_ether.h> | 31 | #include <linux/if_ether.h> |
31 | 32 | ||
32 | #include <net/sock.h> | 33 | #include <net/sock.h> |
@@ -166,38 +167,64 @@ static DEFINE_RWLOCK(ipgre_lock); | |||
166 | /* Given src, dst and key, find appropriate for input tunnel. */ | 167 | /* Given src, dst and key, find appropriate for input tunnel. */ |
167 | 168 | ||
168 | static struct ip_tunnel * ipgre_tunnel_lookup(struct net *net, | 169 | static struct ip_tunnel * ipgre_tunnel_lookup(struct net *net, |
169 | __be32 remote, __be32 local, __be32 key) | 170 | __be32 remote, __be32 local, |
171 | __be32 key, __be16 gre_proto) | ||
170 | { | 172 | { |
171 | unsigned h0 = HASH(remote); | 173 | unsigned h0 = HASH(remote); |
172 | unsigned h1 = HASH(key); | 174 | unsigned h1 = HASH(key); |
173 | struct ip_tunnel *t; | 175 | struct ip_tunnel *t; |
176 | struct ip_tunnel *t2 = NULL; | ||
174 | struct ipgre_net *ign = net_generic(net, ipgre_net_id); | 177 | struct ipgre_net *ign = net_generic(net, ipgre_net_id); |
178 | int dev_type = (gre_proto == htons(ETH_P_TEB)) ? | ||
179 | ARPHRD_ETHER : ARPHRD_IPGRE; | ||
175 | 180 | ||
176 | for (t = ign->tunnels_r_l[h0^h1]; t; t = t->next) { | 181 | for (t = ign->tunnels_r_l[h0^h1]; t; t = t->next) { |
177 | if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) { | 182 | if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) { |
178 | if (t->parms.i_key == key && (t->dev->flags&IFF_UP)) | 183 | if (t->parms.i_key == key && t->dev->flags & IFF_UP) { |
179 | return t; | 184 | if (t->dev->type == dev_type) |
185 | return t; | ||
186 | if (t->dev->type == ARPHRD_IPGRE && !t2) | ||
187 | t2 = t; | ||
188 | } | ||
180 | } | 189 | } |
181 | } | 190 | } |
191 | |||
182 | for (t = ign->tunnels_r[h0^h1]; t; t = t->next) { | 192 | for (t = ign->tunnels_r[h0^h1]; t; t = t->next) { |
183 | if (remote == t->parms.iph.daddr) { | 193 | if (remote == t->parms.iph.daddr) { |
184 | if (t->parms.i_key == key && (t->dev->flags&IFF_UP)) | 194 | if (t->parms.i_key == key && t->dev->flags & IFF_UP) { |
185 | return t; | 195 | if (t->dev->type == dev_type) |
196 | return t; | ||
197 | if (t->dev->type == ARPHRD_IPGRE && !t2) | ||
198 | t2 = t; | ||
199 | } | ||
186 | } | 200 | } |
187 | } | 201 | } |
202 | |||
188 | for (t = ign->tunnels_l[h1]; t; t = t->next) { | 203 | for (t = ign->tunnels_l[h1]; t; t = t->next) { |
189 | if (local == t->parms.iph.saddr || | 204 | if (local == t->parms.iph.saddr || |
190 | (local == t->parms.iph.daddr && | 205 | (local == t->parms.iph.daddr && |
191 | ipv4_is_multicast(local))) { | 206 | ipv4_is_multicast(local))) { |
192 | if (t->parms.i_key == key && (t->dev->flags&IFF_UP)) | 207 | if (t->parms.i_key == key && t->dev->flags & IFF_UP) { |
193 | return t; | 208 | if (t->dev->type == dev_type) |
209 | return t; | ||
210 | if (t->dev->type == ARPHRD_IPGRE && !t2) | ||
211 | t2 = t; | ||
212 | } | ||
194 | } | 213 | } |
195 | } | 214 | } |
215 | |||
196 | for (t = ign->tunnels_wc[h1]; t; t = t->next) { | 216 | for (t = ign->tunnels_wc[h1]; t; t = t->next) { |
197 | if (t->parms.i_key == key && (t->dev->flags&IFF_UP)) | 217 | if (t->parms.i_key == key && t->dev->flags & IFF_UP) { |
198 | return t; | 218 | if (t->dev->type == dev_type) |
219 | return t; | ||
220 | if (t->dev->type == ARPHRD_IPGRE && !t2) | ||
221 | t2 = t; | ||
222 | } | ||
199 | } | 223 | } |
200 | 224 | ||
225 | if (t2) | ||
226 | return t2; | ||
227 | |||
201 | if (ign->fb_tunnel_dev->flags&IFF_UP) | 228 | if (ign->fb_tunnel_dev->flags&IFF_UP) |
202 | return netdev_priv(ign->fb_tunnel_dev); | 229 | return netdev_priv(ign->fb_tunnel_dev); |
203 | return NULL; | 230 | return NULL; |
@@ -252,25 +279,37 @@ static void ipgre_tunnel_unlink(struct ipgre_net *ign, struct ip_tunnel *t) | |||
252 | } | 279 | } |
253 | } | 280 | } |
254 | 281 | ||
255 | static struct ip_tunnel * ipgre_tunnel_locate(struct net *net, | 282 | static struct ip_tunnel *ipgre_tunnel_find(struct net *net, |
256 | struct ip_tunnel_parm *parms, int create) | 283 | struct ip_tunnel_parm *parms, |
284 | int type) | ||
257 | { | 285 | { |
258 | __be32 remote = parms->iph.daddr; | 286 | __be32 remote = parms->iph.daddr; |
259 | __be32 local = parms->iph.saddr; | 287 | __be32 local = parms->iph.saddr; |
260 | __be32 key = parms->i_key; | 288 | __be32 key = parms->i_key; |
261 | struct ip_tunnel *t, **tp, *nt; | 289 | struct ip_tunnel *t, **tp; |
290 | struct ipgre_net *ign = net_generic(net, ipgre_net_id); | ||
291 | |||
292 | for (tp = __ipgre_bucket(ign, parms); (t = *tp) != NULL; tp = &t->next) | ||
293 | if (local == t->parms.iph.saddr && | ||
294 | remote == t->parms.iph.daddr && | ||
295 | key == t->parms.i_key && | ||
296 | type == t->dev->type) | ||
297 | break; | ||
298 | |||
299 | return t; | ||
300 | } | ||
301 | |||
302 | static struct ip_tunnel * ipgre_tunnel_locate(struct net *net, | ||
303 | struct ip_tunnel_parm *parms, int create) | ||
304 | { | ||
305 | struct ip_tunnel *t, *nt; | ||
262 | struct net_device *dev; | 306 | struct net_device *dev; |
263 | char name[IFNAMSIZ]; | 307 | char name[IFNAMSIZ]; |
264 | struct ipgre_net *ign = net_generic(net, ipgre_net_id); | 308 | struct ipgre_net *ign = net_generic(net, ipgre_net_id); |
265 | 309 | ||
266 | for (tp = __ipgre_bucket(ign, parms); (t = *tp) != NULL; tp = &t->next) { | 310 | t = ipgre_tunnel_find(net, parms, ARPHRD_IPGRE); |
267 | if (local == t->parms.iph.saddr && remote == t->parms.iph.daddr) { | 311 | if (t || !create) |
268 | if (key == t->parms.i_key) | 312 | return t; |
269 | return t; | ||
270 | } | ||
271 | } | ||
272 | if (!create) | ||
273 | return NULL; | ||
274 | 313 | ||
275 | if (parms->name[0]) | 314 | if (parms->name[0]) |
276 | strlcpy(name, parms->name, IFNAMSIZ); | 315 | strlcpy(name, parms->name, IFNAMSIZ); |
@@ -385,8 +424,9 @@ static void ipgre_err(struct sk_buff *skb, u32 info) | |||
385 | 424 | ||
386 | read_lock(&ipgre_lock); | 425 | read_lock(&ipgre_lock); |
387 | t = ipgre_tunnel_lookup(dev_net(skb->dev), iph->daddr, iph->saddr, | 426 | t = ipgre_tunnel_lookup(dev_net(skb->dev), iph->daddr, iph->saddr, |
388 | (flags&GRE_KEY) ? | 427 | flags & GRE_KEY ? |
389 | *(((__be32*)p) + (grehlen>>2) - 1) : 0); | 428 | *(((__be32 *)p) + (grehlen / 4) - 1) : 0, |
429 | p[1]); | ||
390 | if (t == NULL || t->parms.iph.daddr == 0 || | 430 | if (t == NULL || t->parms.iph.daddr == 0 || |
391 | ipv4_is_multicast(t->parms.iph.daddr)) | 431 | ipv4_is_multicast(t->parms.iph.daddr)) |
392 | goto out; | 432 | goto out; |
@@ -436,6 +476,7 @@ static int ipgre_rcv(struct sk_buff *skb) | |||
436 | u32 seqno = 0; | 476 | u32 seqno = 0; |
437 | struct ip_tunnel *tunnel; | 477 | struct ip_tunnel *tunnel; |
438 | int offset = 4; | 478 | int offset = 4; |
479 | __be16 gre_proto; | ||
439 | 480 | ||
440 | if (!pskb_may_pull(skb, 16)) | 481 | if (!pskb_may_pull(skb, 16)) |
441 | goto drop_nolock; | 482 | goto drop_nolock; |
@@ -475,20 +516,22 @@ static int ipgre_rcv(struct sk_buff *skb) | |||
475 | } | 516 | } |
476 | } | 517 | } |
477 | 518 | ||
519 | gre_proto = *(__be16 *)(h + 2); | ||
520 | |||
478 | read_lock(&ipgre_lock); | 521 | read_lock(&ipgre_lock); |
479 | if ((tunnel = ipgre_tunnel_lookup(dev_net(skb->dev), | 522 | if ((tunnel = ipgre_tunnel_lookup(dev_net(skb->dev), |
480 | iph->saddr, iph->daddr, key)) != NULL) { | 523 | iph->saddr, iph->daddr, key, |
524 | gre_proto))) { | ||
481 | struct net_device_stats *stats = &tunnel->dev->stats; | 525 | struct net_device_stats *stats = &tunnel->dev->stats; |
482 | 526 | ||
483 | secpath_reset(skb); | 527 | secpath_reset(skb); |
484 | 528 | ||
485 | skb->protocol = *(__be16*)(h + 2); | 529 | skb->protocol = gre_proto; |
486 | /* WCCP version 1 and 2 protocol decoding. | 530 | /* WCCP version 1 and 2 protocol decoding. |
487 | * - Change protocol to IP | 531 | * - Change protocol to IP |
488 | * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header | 532 | * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header |
489 | */ | 533 | */ |
490 | if (flags == 0 && | 534 | if (flags == 0 && gre_proto == htons(ETH_P_WCCP)) { |
491 | skb->protocol == htons(ETH_P_WCCP)) { | ||
492 | skb->protocol = htons(ETH_P_IP); | 535 | skb->protocol = htons(ETH_P_IP); |
493 | if ((*(h + offset) & 0xF0) != 0x40) | 536 | if ((*(h + offset) & 0xF0) != 0x40) |
494 | offset += 4; | 537 | offset += 4; |
@@ -496,7 +539,6 @@ static int ipgre_rcv(struct sk_buff *skb) | |||
496 | 539 | ||
497 | skb->mac_header = skb->network_header; | 540 | skb->mac_header = skb->network_header; |
498 | __pskb_pull(skb, offset); | 541 | __pskb_pull(skb, offset); |
499 | skb_reset_network_header(skb); | ||
500 | skb_postpull_rcsum(skb, skb_transport_header(skb), offset); | 542 | skb_postpull_rcsum(skb, skb_transport_header(skb), offset); |
501 | skb->pkt_type = PACKET_HOST; | 543 | skb->pkt_type = PACKET_HOST; |
502 | #ifdef CONFIG_NET_IPGRE_BROADCAST | 544 | #ifdef CONFIG_NET_IPGRE_BROADCAST |
@@ -524,13 +566,30 @@ static int ipgre_rcv(struct sk_buff *skb) | |||
524 | } | 566 | } |
525 | tunnel->i_seqno = seqno + 1; | 567 | tunnel->i_seqno = seqno + 1; |
526 | } | 568 | } |
569 | |||
570 | /* Warning: All skb pointers will be invalidated! */ | ||
571 | if (tunnel->dev->type == ARPHRD_ETHER) { | ||
572 | if (!pskb_may_pull(skb, ETH_HLEN)) { | ||
573 | stats->rx_length_errors++; | ||
574 | stats->rx_errors++; | ||
575 | goto drop; | ||
576 | } | ||
577 | |||
578 | iph = ip_hdr(skb); | ||
579 | skb->protocol = eth_type_trans(skb, tunnel->dev); | ||
580 | skb_postpull_rcsum(skb, eth_hdr(skb), ETH_HLEN); | ||
581 | } | ||
582 | |||
527 | stats->rx_packets++; | 583 | stats->rx_packets++; |
528 | stats->rx_bytes += skb->len; | 584 | stats->rx_bytes += skb->len; |
529 | skb->dev = tunnel->dev; | 585 | skb->dev = tunnel->dev; |
530 | dst_release(skb->dst); | 586 | dst_release(skb->dst); |
531 | skb->dst = NULL; | 587 | skb->dst = NULL; |
532 | nf_reset(skb); | 588 | nf_reset(skb); |
589 | |||
590 | skb_reset_network_header(skb); | ||
533 | ipgre_ecn_decapsulate(iph, skb); | 591 | ipgre_ecn_decapsulate(iph, skb); |
592 | |||
534 | netif_rx(skb); | 593 | netif_rx(skb); |
535 | read_unlock(&ipgre_lock); | 594 | read_unlock(&ipgre_lock); |
536 | return(0); | 595 | return(0); |
@@ -565,7 +624,10 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) | |||
565 | goto tx_error; | 624 | goto tx_error; |
566 | } | 625 | } |
567 | 626 | ||
568 | if (dev->header_ops) { | 627 | if (dev->type == ARPHRD_ETHER) |
628 | IPCB(skb)->flags = 0; | ||
629 | |||
630 | if (dev->header_ops && dev->type == ARPHRD_IPGRE) { | ||
569 | gre_hlen = 0; | 631 | gre_hlen = 0; |
570 | tiph = (struct iphdr*)skb->data; | 632 | tiph = (struct iphdr*)skb->data; |
571 | } else { | 633 | } else { |
@@ -741,8 +803,9 @@ static int ipgre_tunnel_xmit(struct sk_buff *skb, struct net_device *dev) | |||
741 | iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT); | 803 | iph->ttl = dst_metric(&rt->u.dst, RTAX_HOPLIMIT); |
742 | } | 804 | } |
743 | 805 | ||
744 | ((__be16*)(iph+1))[0] = tunnel->parms.o_flags; | 806 | ((__be16 *)(iph + 1))[0] = tunnel->parms.o_flags; |
745 | ((__be16*)(iph+1))[1] = skb->protocol; | 807 | ((__be16 *)(iph + 1))[1] = (dev->type == ARPHRD_ETHER) ? |
808 | htons(ETH_P_TEB) : skb->protocol; | ||
746 | 809 | ||
747 | if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) { | 810 | if (tunnel->parms.o_flags&(GRE_KEY|GRE_CSUM|GRE_SEQ)) { |
748 | __be32 *ptr = (__be32*)(((u8*)iph) + tunnel->hlen - 4); | 811 | __be32 *ptr = (__be32*)(((u8*)iph) + tunnel->hlen - 4); |
@@ -804,7 +867,9 @@ static int ipgre_tunnel_bind_dev(struct net_device *dev) | |||
804 | tdev = rt->u.dst.dev; | 867 | tdev = rt->u.dst.dev; |
805 | ip_rt_put(rt); | 868 | ip_rt_put(rt); |
806 | } | 869 | } |
807 | dev->flags |= IFF_POINTOPOINT; | 870 | |
871 | if (dev->type != ARPHRD_ETHER) | ||
872 | dev->flags |= IFF_POINTOPOINT; | ||
808 | } | 873 | } |
809 | 874 | ||
810 | if (!tdev && tunnel->parms.link) | 875 | if (!tdev && tunnel->parms.link) |
@@ -1250,6 +1315,30 @@ static int ipgre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[]) | |||
1250 | return 0; | 1315 | return 0; |
1251 | } | 1316 | } |
1252 | 1317 | ||
1318 | static int ipgre_tap_validate(struct nlattr *tb[], struct nlattr *data[]) | ||
1319 | { | ||
1320 | __be32 daddr; | ||
1321 | |||
1322 | if (tb[IFLA_ADDRESS]) { | ||
1323 | if (nla_len(tb[IFLA_ADDRESS]) != ETH_ALEN) | ||
1324 | return -EINVAL; | ||
1325 | if (!is_valid_ether_addr(nla_data(tb[IFLA_ADDRESS]))) | ||
1326 | return -EADDRNOTAVAIL; | ||
1327 | } | ||
1328 | |||
1329 | if (!data) | ||
1330 | goto out; | ||
1331 | |||
1332 | if (data[IFLA_GRE_REMOTE]) { | ||
1333 | memcpy(&daddr, nla_data(data[IFLA_GRE_REMOTE]), 4); | ||
1334 | if (!daddr) | ||
1335 | return -EINVAL; | ||
1336 | } | ||
1337 | |||
1338 | out: | ||
1339 | return ipgre_tunnel_validate(tb, data); | ||
1340 | } | ||
1341 | |||
1253 | static void ipgre_netlink_parms(struct nlattr *data[], | 1342 | static void ipgre_netlink_parms(struct nlattr *data[], |
1254 | struct ip_tunnel_parm *parms) | 1343 | struct ip_tunnel_parm *parms) |
1255 | { | 1344 | { |
@@ -1291,6 +1380,35 @@ static void ipgre_netlink_parms(struct nlattr *data[], | |||
1291 | parms->iph.frag_off = htons(IP_DF); | 1380 | parms->iph.frag_off = htons(IP_DF); |
1292 | } | 1381 | } |
1293 | 1382 | ||
1383 | static int ipgre_tap_init(struct net_device *dev) | ||
1384 | { | ||
1385 | struct ip_tunnel *tunnel; | ||
1386 | |||
1387 | tunnel = netdev_priv(dev); | ||
1388 | |||
1389 | tunnel->dev = dev; | ||
1390 | strcpy(tunnel->parms.name, dev->name); | ||
1391 | |||
1392 | ipgre_tunnel_bind_dev(dev); | ||
1393 | |||
1394 | return 0; | ||
1395 | } | ||
1396 | |||
1397 | static void ipgre_tap_setup(struct net_device *dev) | ||
1398 | { | ||
1399 | |||
1400 | ether_setup(dev); | ||
1401 | |||
1402 | dev->init = ipgre_tap_init; | ||
1403 | dev->uninit = ipgre_tunnel_uninit; | ||
1404 | dev->destructor = free_netdev; | ||
1405 | dev->hard_start_xmit = ipgre_tunnel_xmit; | ||
1406 | dev->change_mtu = ipgre_tunnel_change_mtu; | ||
1407 | |||
1408 | dev->iflink = 0; | ||
1409 | dev->features |= NETIF_F_NETNS_LOCAL; | ||
1410 | } | ||
1411 | |||
1294 | static int ipgre_newlink(struct net_device *dev, struct nlattr *tb[], | 1412 | static int ipgre_newlink(struct net_device *dev, struct nlattr *tb[], |
1295 | struct nlattr *data[]) | 1413 | struct nlattr *data[]) |
1296 | { | 1414 | { |
@@ -1303,9 +1421,12 @@ static int ipgre_newlink(struct net_device *dev, struct nlattr *tb[], | |||
1303 | nt = netdev_priv(dev); | 1421 | nt = netdev_priv(dev); |
1304 | ipgre_netlink_parms(data, &nt->parms); | 1422 | ipgre_netlink_parms(data, &nt->parms); |
1305 | 1423 | ||
1306 | if (ipgre_tunnel_locate(net, &nt->parms, 0)) | 1424 | if (ipgre_tunnel_find(net, &nt->parms, dev->type)) |
1307 | return -EEXIST; | 1425 | return -EEXIST; |
1308 | 1426 | ||
1427 | if (dev->type == ARPHRD_ETHER && !tb[IFLA_ADDRESS]) | ||
1428 | random_ether_addr(dev->dev_addr); | ||
1429 | |||
1309 | mtu = ipgre_tunnel_bind_dev(dev); | 1430 | mtu = ipgre_tunnel_bind_dev(dev); |
1310 | if (!tb[IFLA_MTU]) | 1431 | if (!tb[IFLA_MTU]) |
1311 | dev->mtu = mtu; | 1432 | dev->mtu = mtu; |
@@ -1455,6 +1576,19 @@ static struct rtnl_link_ops ipgre_link_ops __read_mostly = { | |||
1455 | .fill_info = ipgre_fill_info, | 1576 | .fill_info = ipgre_fill_info, |
1456 | }; | 1577 | }; |
1457 | 1578 | ||
1579 | static struct rtnl_link_ops ipgre_tap_ops __read_mostly = { | ||
1580 | .kind = "gretap", | ||
1581 | .maxtype = IFLA_GRE_MAX, | ||
1582 | .policy = ipgre_policy, | ||
1583 | .priv_size = sizeof(struct ip_tunnel), | ||
1584 | .setup = ipgre_tap_setup, | ||
1585 | .validate = ipgre_tap_validate, | ||
1586 | .newlink = ipgre_newlink, | ||
1587 | .changelink = ipgre_changelink, | ||
1588 | .get_size = ipgre_get_size, | ||
1589 | .fill_info = ipgre_fill_info, | ||
1590 | }; | ||
1591 | |||
1458 | /* | 1592 | /* |
1459 | * And now the modules code and kernel interface. | 1593 | * And now the modules code and kernel interface. |
1460 | */ | 1594 | */ |
@@ -1478,9 +1612,15 @@ static int __init ipgre_init(void) | |||
1478 | if (err < 0) | 1612 | if (err < 0) |
1479 | goto rtnl_link_failed; | 1613 | goto rtnl_link_failed; |
1480 | 1614 | ||
1615 | err = rtnl_link_register(&ipgre_tap_ops); | ||
1616 | if (err < 0) | ||
1617 | goto tap_ops_failed; | ||
1618 | |||
1481 | out: | 1619 | out: |
1482 | return err; | 1620 | return err; |
1483 | 1621 | ||
1622 | tap_ops_failed: | ||
1623 | rtnl_link_unregister(&ipgre_link_ops); | ||
1484 | rtnl_link_failed: | 1624 | rtnl_link_failed: |
1485 | unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops); | 1625 | unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops); |
1486 | gen_device_failed: | 1626 | gen_device_failed: |
@@ -1490,6 +1630,7 @@ gen_device_failed: | |||
1490 | 1630 | ||
1491 | static void __exit ipgre_fini(void) | 1631 | static void __exit ipgre_fini(void) |
1492 | { | 1632 | { |
1633 | rtnl_link_unregister(&ipgre_tap_ops); | ||
1493 | rtnl_link_unregister(&ipgre_link_ops); | 1634 | rtnl_link_unregister(&ipgre_link_ops); |
1494 | unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops); | 1635 | unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops); |
1495 | if (inet_del_protocol(&ipgre_protocol, IPPROTO_GRE) < 0) | 1636 | if (inet_del_protocol(&ipgre_protocol, IPPROTO_GRE) < 0) |
@@ -1500,3 +1641,4 @@ module_init(ipgre_init); | |||
1500 | module_exit(ipgre_fini); | 1641 | module_exit(ipgre_fini); |
1501 | MODULE_LICENSE("GPL"); | 1642 | MODULE_LICENSE("GPL"); |
1502 | MODULE_ALIAS("rtnl-link-gre"); | 1643 | MODULE_ALIAS("rtnl-link-gre"); |
1644 | MODULE_ALIAS("rtnl-link-gretap"); | ||