aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorPravin B Shelar <pshelar@nicira.com>2013-06-17 20:49:38 -0400
committerDavid S. Miller <davem@davemloft.net>2013-06-19 21:07:40 -0400
commitbda7bb46343647f68591366731295a0f3eea59ed (patch)
tree99fa502fe0439a7776227238bfc3a5b0d4dbe7b3 /net
parent20fd4d1f04da07d09192ad8ad366a70d5125bfaf (diff)
gre: Allow multiple protocol listener for gre protocol.
Currently there is only one user is allowed to register for gre protocol. Following patch adds de-multiplexer. So that multiple modules can listen on gre protocol e.g. kernel gre devices and ovs. Signed-off-by: Pravin B Shelar <pshelar@nicira.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/ipv4/gre.c221
-rw-r--r--net/ipv4/ip_gre.c173
2 files changed, 243 insertions, 151 deletions
diff --git a/net/ipv4/gre.c b/net/ipv4/gre.c
index 1e294d510ac3..8b9a373890ab 100644
--- a/net/ipv4/gre.c
+++ b/net/ipv4/gre.c
@@ -13,6 +13,8 @@
13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14 14
15#include <linux/module.h> 15#include <linux/module.h>
16#include <linux/if.h>
17#include <linux/icmp.h>
16#include <linux/kernel.h> 18#include <linux/kernel.h>
17#include <linux/kmod.h> 19#include <linux/kmod.h>
18#include <linux/skbuff.h> 20#include <linux/skbuff.h>
@@ -24,8 +26,12 @@
24#include <net/protocol.h> 26#include <net/protocol.h>
25#include <net/gre.h> 27#include <net/gre.h>
26 28
29#include <net/icmp.h>
30#include <net/route.h>
31#include <net/xfrm.h>
27 32
28static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly; 33static const struct gre_protocol __rcu *gre_proto[GREPROTO_MAX] __read_mostly;
34static struct gre_cisco_protocol __rcu *gre_cisco_proto_list[GRE_IP_PROTO_MAX];
29 35
30int gre_add_protocol(const struct gre_protocol *proto, u8 version) 36int gre_add_protocol(const struct gre_protocol *proto, u8 version)
31{ 37{
@@ -55,6 +61,173 @@ int gre_del_protocol(const struct gre_protocol *proto, u8 version)
55} 61}
56EXPORT_SYMBOL_GPL(gre_del_protocol); 62EXPORT_SYMBOL_GPL(gre_del_protocol);
57 63
64static __sum16 check_checksum(struct sk_buff *skb)
65{
66 __sum16 csum = 0;
67
68 switch (skb->ip_summed) {
69 case CHECKSUM_COMPLETE:
70 csum = csum_fold(skb->csum);
71
72 if (!csum)
73 break;
74 /* Fall through. */
75
76 case CHECKSUM_NONE:
77 skb->csum = 0;
78 csum = __skb_checksum_complete(skb);
79 skb->ip_summed = CHECKSUM_COMPLETE;
80 break;
81 }
82
83 return csum;
84}
85
86static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
87 bool *csum_err)
88{
89 unsigned int ip_hlen = ip_hdrlen(skb);
90 const struct gre_base_hdr *greh;
91 __be32 *options;
92 int hdr_len;
93
94 if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr))))
95 return -EINVAL;
96
97 greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
98 if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
99 return -EINVAL;
100
101 tpi->flags = gre_flags_to_tnl_flags(greh->flags);
102 hdr_len = ip_gre_calc_hlen(tpi->flags);
103
104 if (!pskb_may_pull(skb, hdr_len))
105 return -EINVAL;
106
107 greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
108 tpi->proto = greh->protocol;
109
110 options = (__be32 *)(greh + 1);
111 if (greh->flags & GRE_CSUM) {
112 if (check_checksum(skb)) {
113 *csum_err = true;
114 return -EINVAL;
115 }
116 options++;
117 }
118
119 if (greh->flags & GRE_KEY) {
120 tpi->key = *options;
121 options++;
122 } else
123 tpi->key = 0;
124
125 if (unlikely(greh->flags & GRE_SEQ)) {
126 tpi->seq = *options;
127 options++;
128 } else
129 tpi->seq = 0;
130
131 /* WCCP version 1 and 2 protocol decoding.
132 * - Change protocol to IP
133 * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
134 */
135 if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) {
136 tpi->proto = htons(ETH_P_IP);
137 if ((*(u8 *)options & 0xF0) != 0x40) {
138 hdr_len += 4;
139 if (!pskb_may_pull(skb, hdr_len))
140 return -EINVAL;
141 }
142 }
143 return 0;
144}
145
146static int gre_cisco_rcv(struct sk_buff *skb)
147{
148 struct tnl_ptk_info tpi;
149 int i;
150 bool csum_err = false;
151
152 if (parse_gre_header(skb, &tpi, &csum_err) < 0)
153 goto drop;
154
155 rcu_read_lock();
156 for (i = 0; i < GRE_IP_PROTO_MAX; i++) {
157 struct gre_cisco_protocol *proto;
158 int ret;
159
160 proto = rcu_dereference(gre_cisco_proto_list[i]);
161 if (!proto)
162 continue;
163 ret = proto->handler(skb, &tpi);
164 if (ret == PACKET_RCVD) {
165 rcu_read_unlock();
166 return 0;
167 }
168 }
169 rcu_read_unlock();
170
171 icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0);
172drop:
173 kfree_skb(skb);
174 return 0;
175}
176
177static void gre_cisco_err(struct sk_buff *skb, u32 info)
178{
179 /* All the routers (except for Linux) return only
180 * 8 bytes of packet payload. It means, that precise relaying of
181 * ICMP in the real Internet is absolutely infeasible.
182 *
183 * Moreover, Cisco "wise men" put GRE key to the third word
184 * in GRE header. It makes impossible maintaining even soft
185 * state for keyed
186 * GRE tunnels with enabled checksum. Tell them "thank you".
187 *
188 * Well, I wonder, rfc1812 was written by Cisco employee,
189 * what the hell these idiots break standards established
190 * by themselves???
191 */
192
193 const int type = icmp_hdr(skb)->type;
194 const int code = icmp_hdr(skb)->code;
195 struct tnl_ptk_info tpi;
196 bool csum_err = false;
197 int i;
198
199 if (parse_gre_header(skb, &tpi, &csum_err)) {
200 if (!csum_err) /* ignore csum errors. */
201 return;
202 }
203
204 if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
205 ipv4_update_pmtu(skb, dev_net(skb->dev), info,
206 skb->dev->ifindex, 0, IPPROTO_GRE, 0);
207 return;
208 }
209 if (type == ICMP_REDIRECT) {
210 ipv4_redirect(skb, dev_net(skb->dev), skb->dev->ifindex, 0,
211 IPPROTO_GRE, 0);
212 return;
213 }
214
215 rcu_read_lock();
216 for (i = 0; i < GRE_IP_PROTO_MAX; i++) {
217 struct gre_cisco_protocol *proto;
218
219 proto = rcu_dereference(gre_cisco_proto_list[i]);
220 if (!proto)
221 continue;
222
223 if (proto->err_handler(skb, info, &tpi) == PACKET_RCVD)
224 goto out;
225
226 }
227out:
228 rcu_read_unlock();
229}
230
58static int gre_rcv(struct sk_buff *skb) 231static int gre_rcv(struct sk_buff *skb)
59{ 232{
60 const struct gre_protocol *proto; 233 const struct gre_protocol *proto;
@@ -206,27 +379,68 @@ static const struct net_offload gre_offload = {
206 }, 379 },
207}; 380};
208 381
382static const struct gre_protocol ipgre_protocol = {
383 .handler = gre_cisco_rcv,
384 .err_handler = gre_cisco_err,
385};
386
387int gre_cisco_register(struct gre_cisco_protocol *newp)
388{
389 struct gre_cisco_protocol **proto = (struct gre_cisco_protocol **)
390 &gre_cisco_proto_list[newp->priority];
391
392 return (cmpxchg(proto, NULL, newp) == NULL) ? 0 : -EBUSY;
393}
394EXPORT_SYMBOL_GPL(gre_cisco_register);
395
396int gre_cisco_unregister(struct gre_cisco_protocol *del_proto)
397{
398 struct gre_cisco_protocol **proto = (struct gre_cisco_protocol **)
399 &gre_cisco_proto_list[del_proto->priority];
400 int ret;
401
402 ret = (cmpxchg(proto, del_proto, NULL) == del_proto) ? 0 : -EINVAL;
403
404 if (ret)
405 return ret;
406
407 synchronize_net();
408 return 0;
409}
410EXPORT_SYMBOL_GPL(gre_cisco_unregister);
411
209static int __init gre_init(void) 412static int __init gre_init(void)
210{ 413{
211 pr_info("GRE over IPv4 demultiplexor driver\n"); 414 pr_info("GRE over IPv4 demultiplexor driver\n");
212 415
213 if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) { 416 if (inet_add_protocol(&net_gre_protocol, IPPROTO_GRE) < 0) {
214 pr_err("can't add protocol\n"); 417 pr_err("can't add protocol\n");
215 return -EAGAIN; 418 goto err;
419 }
420
421 if (gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO) < 0) {
422 pr_info("%s: can't add ipgre handler\n", __func__);
423 goto err_gre;
216 } 424 }
217 425
218 if (inet_add_offload(&gre_offload, IPPROTO_GRE)) { 426 if (inet_add_offload(&gre_offload, IPPROTO_GRE)) {
219 pr_err("can't add protocol offload\n"); 427 pr_err("can't add protocol offload\n");
220 inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); 428 goto err_gso;
221 return -EAGAIN;
222 } 429 }
223 430
224 return 0; 431 return 0;
432err_gso:
433 gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
434err_gre:
435 inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
436err:
437 return -EAGAIN;
225} 438}
226 439
227static void __exit gre_exit(void) 440static void __exit gre_exit(void)
228{ 441{
229 inet_del_offload(&gre_offload, IPPROTO_GRE); 442 inet_del_offload(&gre_offload, IPPROTO_GRE);
443 gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO);
230 inet_del_protocol(&net_gre_protocol, IPPROTO_GRE); 444 inet_del_protocol(&net_gre_protocol, IPPROTO_GRE);
231} 445}
232 446
@@ -236,4 +450,3 @@ module_exit(gre_exit);
236MODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver"); 450MODULE_DESCRIPTION("GRE over IPv4 demultiplexer driver");
237MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)"); 451MODULE_AUTHOR("D. Kozlov (xeb@mail.ru)");
238MODULE_LICENSE("GPL"); 452MODULE_LICENSE("GPL");
239
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c
index a982657d05e7..19863a81cea1 100644
--- a/net/ipv4/ip_gre.c
+++ b/net/ipv4/ip_gre.c
@@ -121,103 +121,8 @@ static int ipgre_tunnel_init(struct net_device *dev);
121static int ipgre_net_id __read_mostly; 121static int ipgre_net_id __read_mostly;
122static int gre_tap_net_id __read_mostly; 122static int gre_tap_net_id __read_mostly;
123 123
124static __sum16 check_checksum(struct sk_buff *skb) 124static int ipgre_err(struct sk_buff *skb, u32 info,
125{ 125 const struct tnl_ptk_info *tpi)
126 __sum16 csum = 0;
127
128 switch (skb->ip_summed) {
129 case CHECKSUM_COMPLETE:
130 csum = csum_fold(skb->csum);
131
132 if (!csum)
133 break;
134 /* Fall through. */
135
136 case CHECKSUM_NONE:
137 skb->csum = 0;
138 csum = __skb_checksum_complete(skb);
139 skb->ip_summed = CHECKSUM_COMPLETE;
140 break;
141 }
142
143 return csum;
144}
145
146static int ip_gre_calc_hlen(__be16 o_flags)
147{
148 int addend = 4;
149
150 if (o_flags&TUNNEL_CSUM)
151 addend += 4;
152 if (o_flags&TUNNEL_KEY)
153 addend += 4;
154 if (o_flags&TUNNEL_SEQ)
155 addend += 4;
156 return addend;
157}
158
159static int parse_gre_header(struct sk_buff *skb, struct tnl_ptk_info *tpi,
160 bool *csum_err, int *hdr_len)
161{
162 unsigned int ip_hlen = ip_hdrlen(skb);
163 const struct gre_base_hdr *greh;
164 __be32 *options;
165
166 if (unlikely(!pskb_may_pull(skb, sizeof(struct gre_base_hdr))))
167 return -EINVAL;
168
169 greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
170 if (unlikely(greh->flags & (GRE_VERSION | GRE_ROUTING)))
171 return -EINVAL;
172
173 tpi->flags = gre_flags_to_tnl_flags(greh->flags);
174 *hdr_len = ip_gre_calc_hlen(tpi->flags);
175
176 if (!pskb_may_pull(skb, *hdr_len))
177 return -EINVAL;
178
179 greh = (struct gre_base_hdr *)(skb_network_header(skb) + ip_hlen);
180
181 tpi->proto = greh->protocol;
182
183 options = (__be32 *)(greh + 1);
184 if (greh->flags & GRE_CSUM) {
185 if (check_checksum(skb)) {
186 *csum_err = true;
187 return -EINVAL;
188 }
189 options++;
190 }
191
192 if (greh->flags & GRE_KEY) {
193 tpi->key = *options;
194 options++;
195 } else
196 tpi->key = 0;
197
198 if (unlikely(greh->flags & GRE_SEQ)) {
199 tpi->seq = *options;
200 options++;
201 } else
202 tpi->seq = 0;
203
204 /* WCCP version 1 and 2 protocol decoding.
205 * - Change protocol to IP
206 * - When dealing with WCCPv2, Skip extra 4 bytes in GRE header
207 */
208 if (greh->flags == 0 && tpi->proto == htons(ETH_P_WCCP)) {
209 tpi->proto = htons(ETH_P_IP);
210 if ((*(u8 *)options & 0xF0) != 0x40) {
211 *hdr_len += 4;
212 if (!pskb_may_pull(skb, *hdr_len))
213 return -EINVAL;
214 }
215 }
216
217 return 0;
218}
219
220static void ipgre_err(struct sk_buff *skb, u32 info)
221{ 126{
222 127
223 /* All the routers (except for Linux) return only 128 /* All the routers (except for Linux) return only
@@ -239,26 +144,18 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
239 const int type = icmp_hdr(skb)->type; 144 const int type = icmp_hdr(skb)->type;
240 const int code = icmp_hdr(skb)->code; 145 const int code = icmp_hdr(skb)->code;
241 struct ip_tunnel *t; 146 struct ip_tunnel *t;
242 struct tnl_ptk_info tpi;
243 int hdr_len;
244 bool csum_err = false;
245
246 if (parse_gre_header(skb, &tpi, &csum_err, &hdr_len)) {
247 if (!csum_err) /* ignore csum errors. */
248 return;
249 }
250 147
251 switch (type) { 148 switch (type) {
252 default: 149 default:
253 case ICMP_PARAMETERPROB: 150 case ICMP_PARAMETERPROB:
254 return; 151 return PACKET_RCVD;
255 152
256 case ICMP_DEST_UNREACH: 153 case ICMP_DEST_UNREACH:
257 switch (code) { 154 switch (code) {
258 case ICMP_SR_FAILED: 155 case ICMP_SR_FAILED:
259 case ICMP_PORT_UNREACH: 156 case ICMP_PORT_UNREACH:
260 /* Impossible event. */ 157 /* Impossible event. */
261 return; 158 return PACKET_RCVD;
262 default: 159 default:
263 /* All others are translated to HOST_UNREACH. 160 /* All others are translated to HOST_UNREACH.
264 rfc2003 contains "deep thoughts" about NET_UNREACH, 161 rfc2003 contains "deep thoughts" about NET_UNREACH,
@@ -269,79 +166,61 @@ static void ipgre_err(struct sk_buff *skb, u32 info)
269 break; 166 break;
270 case ICMP_TIME_EXCEEDED: 167 case ICMP_TIME_EXCEEDED:
271 if (code != ICMP_EXC_TTL) 168 if (code != ICMP_EXC_TTL)
272 return; 169 return PACKET_RCVD;
273 break; 170 break;
274 171
275 case ICMP_REDIRECT: 172 case ICMP_REDIRECT:
276 break; 173 break;
277 } 174 }
278 175
279 if (tpi.proto == htons(ETH_P_TEB)) 176 if (tpi->proto == htons(ETH_P_TEB))
280 itn = net_generic(net, gre_tap_net_id); 177 itn = net_generic(net, gre_tap_net_id);
281 else 178 else
282 itn = net_generic(net, ipgre_net_id); 179 itn = net_generic(net, ipgre_net_id);
283 180
284 iph = (const struct iphdr *)skb->data; 181 iph = (const struct iphdr *)skb->data;
285 t = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi.flags, 182 t = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi->flags,
286 iph->daddr, iph->saddr, tpi.key); 183 iph->daddr, iph->saddr, tpi->key);
287 184
288 if (t == NULL) 185 if (t == NULL)
289 return; 186 return PACKET_REJECT;
290 187
291 if (type == ICMP_DEST_UNREACH && code == ICMP_FRAG_NEEDED) {
292 ipv4_update_pmtu(skb, dev_net(skb->dev), info,
293 t->parms.link, 0, IPPROTO_GRE, 0);
294 return;
295 }
296 if (type == ICMP_REDIRECT) {
297 ipv4_redirect(skb, dev_net(skb->dev), t->parms.link, 0,
298 IPPROTO_GRE, 0);
299 return;
300 }
301 if (t->parms.iph.daddr == 0 || 188 if (t->parms.iph.daddr == 0 ||
302 ipv4_is_multicast(t->parms.iph.daddr)) 189 ipv4_is_multicast(t->parms.iph.daddr))
303 return; 190 return PACKET_RCVD;
304 191
305 if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED) 192 if (t->parms.iph.ttl == 0 && type == ICMP_TIME_EXCEEDED)
306 return; 193 return PACKET_RCVD;
307 194
308 if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO)) 195 if (time_before(jiffies, t->err_time + IPTUNNEL_ERR_TIMEO))
309 t->err_count++; 196 t->err_count++;
310 else 197 else
311 t->err_count = 1; 198 t->err_count = 1;
312 t->err_time = jiffies; 199 t->err_time = jiffies;
200 return PACKET_RCVD;
313} 201}
314 202
315static int ipgre_rcv(struct sk_buff *skb) 203static int ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi)
316{ 204{
317 struct net *net = dev_net(skb->dev); 205 struct net *net = dev_net(skb->dev);
318 struct ip_tunnel_net *itn; 206 struct ip_tunnel_net *itn;
319 const struct iphdr *iph; 207 const struct iphdr *iph;
320 struct ip_tunnel *tunnel; 208 struct ip_tunnel *tunnel;
321 struct tnl_ptk_info tpi;
322 int hdr_len;
323 bool csum_err = false;
324
325 if (parse_gre_header(skb, &tpi, &csum_err, &hdr_len) < 0)
326 goto drop;
327 209
328 if (tpi.proto == htons(ETH_P_TEB)) 210 if (tpi->proto == htons(ETH_P_TEB))
329 itn = net_generic(net, gre_tap_net_id); 211 itn = net_generic(net, gre_tap_net_id);
330 else 212 else
331 itn = net_generic(net, ipgre_net_id); 213 itn = net_generic(net, ipgre_net_id);
332 214
333 iph = ip_hdr(skb); 215 iph = ip_hdr(skb);
334 tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi.flags, 216 tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, tpi->flags,
335 iph->saddr, iph->daddr, tpi.key); 217 iph->saddr, iph->daddr, tpi->key);
336 218
337 if (tunnel) { 219 if (tunnel) {
338 ip_tunnel_rcv(tunnel, skb, &tpi, log_ecn_error); 220 ip_tunnel_rcv(tunnel, skb, tpi, log_ecn_error);
339 return 0; 221 return PACKET_RCVD;
340 } 222 }
341 icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PORT_UNREACH, 0); 223 return PACKET_REJECT;
342drop:
343 kfree_skb(skb);
344 return 0;
345} 224}
346 225
347static struct sk_buff *handle_offloads(struct ip_tunnel *tunnel, struct sk_buff *skb) 226static struct sk_buff *handle_offloads(struct ip_tunnel *tunnel, struct sk_buff *skb)
@@ -708,9 +587,10 @@ static int ipgre_tunnel_init(struct net_device *dev)
708 return ip_tunnel_init(dev); 587 return ip_tunnel_init(dev);
709} 588}
710 589
711static const struct gre_protocol ipgre_protocol = { 590static struct gre_cisco_protocol ipgre_protocol = {
712 .handler = ipgre_rcv, 591 .handler = ipgre_rcv,
713 .err_handler = ipgre_err, 592 .err_handler = ipgre_err,
593 .priority = 0,
714}; 594};
715 595
716static int __net_init ipgre_init_net(struct net *net) 596static int __net_init ipgre_init_net(struct net *net)
@@ -978,7 +858,7 @@ static int __init ipgre_init(void)
978 if (err < 0) 858 if (err < 0)
979 goto pnet_tap_faied; 859 goto pnet_tap_faied;
980 860
981 err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO); 861 err = gre_cisco_register(&ipgre_protocol);
982 if (err < 0) { 862 if (err < 0) {
983 pr_info("%s: can't add protocol\n", __func__); 863 pr_info("%s: can't add protocol\n", __func__);
984 goto add_proto_failed; 864 goto add_proto_failed;
@@ -997,7 +877,7 @@ static int __init ipgre_init(void)
997tap_ops_failed: 877tap_ops_failed:
998 rtnl_link_unregister(&ipgre_link_ops); 878 rtnl_link_unregister(&ipgre_link_ops);
999rtnl_link_failed: 879rtnl_link_failed:
1000 gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO); 880 gre_cisco_unregister(&ipgre_protocol);
1001add_proto_failed: 881add_proto_failed:
1002 unregister_pernet_device(&ipgre_tap_net_ops); 882 unregister_pernet_device(&ipgre_tap_net_ops);
1003pnet_tap_faied: 883pnet_tap_faied:
@@ -1009,8 +889,7 @@ static void __exit ipgre_fini(void)
1009{ 889{
1010 rtnl_link_unregister(&ipgre_tap_ops); 890 rtnl_link_unregister(&ipgre_tap_ops);
1011 rtnl_link_unregister(&ipgre_link_ops); 891 rtnl_link_unregister(&ipgre_link_ops);
1012 if (gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO) < 0) 892 gre_cisco_unregister(&ipgre_protocol);
1013 pr_info("%s: can't remove protocol\n", __func__);
1014 unregister_pernet_device(&ipgre_tap_net_ops); 893 unregister_pernet_device(&ipgre_tap_net_ops);
1015 unregister_pernet_device(&ipgre_net_ops); 894 unregister_pernet_device(&ipgre_net_ops);
1016} 895}