diff options
author | xeb@mail.ru <xeb@mail.ru> | 2012-08-09 20:51:50 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-08-14 17:28:32 -0400 |
commit | c12b395a46646bab69089ce7016ac78177f6001f (patch) | |
tree | ec3bbef6602f42853b3dad5ae36c7d3872fa56eb /net/ipv6/ip6_tunnel.c | |
parent | b7bc2a5b5bd99b216c3e5fe68c7f45c684ab5745 (diff) |
gre: Support GRE over IPv6
GRE over IPv6 implementation.
Signed-off-by: Dmitry Kozlov <xeb@mail.ru>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/ip6_tunnel.c')
-rw-r--r-- | net/ipv6/ip6_tunnel.c | 89 |
1 files changed, 65 insertions, 24 deletions
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index 9a1d5fe6aef8..33d2a0e6712d 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c | |||
@@ -126,7 +126,7 @@ static struct net_device_stats *ip6_get_stats(struct net_device *dev) | |||
126 | * Locking : hash tables are protected by RCU and RTNL | 126 | * Locking : hash tables are protected by RCU and RTNL |
127 | */ | 127 | */ |
128 | 128 | ||
129 | static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t) | 129 | struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t) |
130 | { | 130 | { |
131 | struct dst_entry *dst = t->dst_cache; | 131 | struct dst_entry *dst = t->dst_cache; |
132 | 132 | ||
@@ -139,20 +139,23 @@ static inline struct dst_entry *ip6_tnl_dst_check(struct ip6_tnl *t) | |||
139 | 139 | ||
140 | return dst; | 140 | return dst; |
141 | } | 141 | } |
142 | EXPORT_SYMBOL_GPL(ip6_tnl_dst_check); | ||
142 | 143 | ||
143 | static inline void ip6_tnl_dst_reset(struct ip6_tnl *t) | 144 | void ip6_tnl_dst_reset(struct ip6_tnl *t) |
144 | { | 145 | { |
145 | dst_release(t->dst_cache); | 146 | dst_release(t->dst_cache); |
146 | t->dst_cache = NULL; | 147 | t->dst_cache = NULL; |
147 | } | 148 | } |
149 | EXPORT_SYMBOL_GPL(ip6_tnl_dst_reset); | ||
148 | 150 | ||
149 | static inline void ip6_tnl_dst_store(struct ip6_tnl *t, struct dst_entry *dst) | 151 | void ip6_tnl_dst_store(struct ip6_tnl *t, struct dst_entry *dst) |
150 | { | 152 | { |
151 | struct rt6_info *rt = (struct rt6_info *) dst; | 153 | struct rt6_info *rt = (struct rt6_info *) dst; |
152 | t->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; | 154 | t->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; |
153 | dst_release(t->dst_cache); | 155 | dst_release(t->dst_cache); |
154 | t->dst_cache = dst; | 156 | t->dst_cache = dst; |
155 | } | 157 | } |
158 | EXPORT_SYMBOL_GPL(ip6_tnl_dst_store); | ||
156 | 159 | ||
157 | /** | 160 | /** |
158 | * ip6_tnl_lookup - fetch tunnel matching the end-point addresses | 161 | * ip6_tnl_lookup - fetch tunnel matching the end-point addresses |
@@ -200,7 +203,7 @@ ip6_tnl_lookup(struct net *net, const struct in6_addr *remote, const struct in6_ | |||
200 | **/ | 203 | **/ |
201 | 204 | ||
202 | static struct ip6_tnl __rcu ** | 205 | static struct ip6_tnl __rcu ** |
203 | ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct ip6_tnl_parm *p) | 206 | ip6_tnl_bucket(struct ip6_tnl_net *ip6n, const struct __ip6_tnl_parm *p) |
204 | { | 207 | { |
205 | const struct in6_addr *remote = &p->raddr; | 208 | const struct in6_addr *remote = &p->raddr; |
206 | const struct in6_addr *local = &p->laddr; | 209 | const struct in6_addr *local = &p->laddr; |
@@ -267,7 +270,7 @@ static void ip6_dev_free(struct net_device *dev) | |||
267 | * created tunnel or NULL | 270 | * created tunnel or NULL |
268 | **/ | 271 | **/ |
269 | 272 | ||
270 | static struct ip6_tnl *ip6_tnl_create(struct net *net, struct ip6_tnl_parm *p) | 273 | static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p) |
271 | { | 274 | { |
272 | struct net_device *dev; | 275 | struct net_device *dev; |
273 | struct ip6_tnl *t; | 276 | struct ip6_tnl *t; |
@@ -322,7 +325,7 @@ failed: | |||
322 | **/ | 325 | **/ |
323 | 326 | ||
324 | static struct ip6_tnl *ip6_tnl_locate(struct net *net, | 327 | static struct ip6_tnl *ip6_tnl_locate(struct net *net, |
325 | struct ip6_tnl_parm *p, int create) | 328 | struct __ip6_tnl_parm *p, int create) |
326 | { | 329 | { |
327 | const struct in6_addr *remote = &p->raddr; | 330 | const struct in6_addr *remote = &p->raddr; |
328 | const struct in6_addr *local = &p->laddr; | 331 | const struct in6_addr *local = &p->laddr; |
@@ -374,8 +377,7 @@ ip6_tnl_dev_uninit(struct net_device *dev) | |||
374 | * else index to encapsulation limit | 377 | * else index to encapsulation limit |
375 | **/ | 378 | **/ |
376 | 379 | ||
377 | static __u16 | 380 | __u16 ip6_tnl_parse_tlv_enc_lim(struct sk_buff *skb, __u8 *raw) |
378 | parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw) | ||
379 | { | 381 | { |
380 | const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) raw; | 382 | const struct ipv6hdr *ipv6h = (const struct ipv6hdr *) raw; |
381 | __u8 nexthdr = ipv6h->nexthdr; | 383 | __u8 nexthdr = ipv6h->nexthdr; |
@@ -425,6 +427,7 @@ parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw) | |||
425 | } | 427 | } |
426 | return 0; | 428 | return 0; |
427 | } | 429 | } |
430 | EXPORT_SYMBOL(ip6_tnl_parse_tlv_enc_lim); | ||
428 | 431 | ||
429 | /** | 432 | /** |
430 | * ip6_tnl_err - tunnel error handler | 433 | * ip6_tnl_err - tunnel error handler |
@@ -480,7 +483,7 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt, | |||
480 | case ICMPV6_PARAMPROB: | 483 | case ICMPV6_PARAMPROB: |
481 | teli = 0; | 484 | teli = 0; |
482 | if ((*code) == ICMPV6_HDR_FIELD) | 485 | if ((*code) == ICMPV6_HDR_FIELD) |
483 | teli = parse_tlv_tnl_enc_lim(skb, skb->data); | 486 | teli = ip6_tnl_parse_tlv_enc_lim(skb, skb->data); |
484 | 487 | ||
485 | if (teli && teli == *info - 2) { | 488 | if (teli && teli == *info - 2) { |
486 | tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli]; | 489 | tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli]; |
@@ -693,11 +696,11 @@ static void ip6ip6_dscp_ecn_decapsulate(const struct ip6_tnl *t, | |||
693 | IP6_ECN_set_ce(ipv6_hdr(skb)); | 696 | IP6_ECN_set_ce(ipv6_hdr(skb)); |
694 | } | 697 | } |
695 | 698 | ||
696 | static __u32 ip6_tnl_get_cap(struct ip6_tnl *t, | 699 | __u32 ip6_tnl_get_cap(struct ip6_tnl *t, |
697 | const struct in6_addr *laddr, | 700 | const struct in6_addr *laddr, |
698 | const struct in6_addr *raddr) | 701 | const struct in6_addr *raddr) |
699 | { | 702 | { |
700 | struct ip6_tnl_parm *p = &t->parms; | 703 | struct __ip6_tnl_parm *p = &t->parms; |
701 | int ltype = ipv6_addr_type(laddr); | 704 | int ltype = ipv6_addr_type(laddr); |
702 | int rtype = ipv6_addr_type(raddr); | 705 | int rtype = ipv6_addr_type(raddr); |
703 | __u32 flags = 0; | 706 | __u32 flags = 0; |
@@ -715,13 +718,14 @@ static __u32 ip6_tnl_get_cap(struct ip6_tnl *t, | |||
715 | } | 718 | } |
716 | return flags; | 719 | return flags; |
717 | } | 720 | } |
721 | EXPORT_SYMBOL(ip6_tnl_get_cap); | ||
718 | 722 | ||
719 | /* called with rcu_read_lock() */ | 723 | /* called with rcu_read_lock() */ |
720 | static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t, | 724 | int ip6_tnl_rcv_ctl(struct ip6_tnl *t, |
721 | const struct in6_addr *laddr, | 725 | const struct in6_addr *laddr, |
722 | const struct in6_addr *raddr) | 726 | const struct in6_addr *raddr) |
723 | { | 727 | { |
724 | struct ip6_tnl_parm *p = &t->parms; | 728 | struct __ip6_tnl_parm *p = &t->parms; |
725 | int ret = 0; | 729 | int ret = 0; |
726 | struct net *net = dev_net(t->dev); | 730 | struct net *net = dev_net(t->dev); |
727 | 731 | ||
@@ -740,6 +744,7 @@ static inline int ip6_tnl_rcv_ctl(struct ip6_tnl *t, | |||
740 | } | 744 | } |
741 | return ret; | 745 | return ret; |
742 | } | 746 | } |
747 | EXPORT_SYMBOL_GPL(ip6_tnl_rcv_ctl); | ||
743 | 748 | ||
744 | /** | 749 | /** |
745 | * ip6_tnl_rcv - decapsulate IPv6 packet and retransmit it locally | 750 | * ip6_tnl_rcv - decapsulate IPv6 packet and retransmit it locally |
@@ -859,9 +864,9 @@ ip6_tnl_addr_conflict(const struct ip6_tnl *t, const struct ipv6hdr *hdr) | |||
859 | return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr); | 864 | return ipv6_addr_equal(&t->parms.raddr, &hdr->saddr); |
860 | } | 865 | } |
861 | 866 | ||
862 | static inline int ip6_tnl_xmit_ctl(struct ip6_tnl *t) | 867 | int ip6_tnl_xmit_ctl(struct ip6_tnl *t) |
863 | { | 868 | { |
864 | struct ip6_tnl_parm *p = &t->parms; | 869 | struct __ip6_tnl_parm *p = &t->parms; |
865 | int ret = 0; | 870 | int ret = 0; |
866 | struct net *net = dev_net(t->dev); | 871 | struct net *net = dev_net(t->dev); |
867 | 872 | ||
@@ -885,6 +890,8 @@ static inline int ip6_tnl_xmit_ctl(struct ip6_tnl *t) | |||
885 | } | 890 | } |
886 | return ret; | 891 | return ret; |
887 | } | 892 | } |
893 | EXPORT_SYMBOL_GPL(ip6_tnl_xmit_ctl); | ||
894 | |||
888 | /** | 895 | /** |
889 | * ip6_tnl_xmit2 - encapsulate packet and send | 896 | * ip6_tnl_xmit2 - encapsulate packet and send |
890 | * @skb: the outgoing socket buffer | 897 | * @skb: the outgoing socket buffer |
@@ -1085,7 +1092,7 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) | |||
1085 | !ip6_tnl_xmit_ctl(t) || ip6_tnl_addr_conflict(t, ipv6h)) | 1092 | !ip6_tnl_xmit_ctl(t) || ip6_tnl_addr_conflict(t, ipv6h)) |
1086 | return -1; | 1093 | return -1; |
1087 | 1094 | ||
1088 | offset = parse_tlv_tnl_enc_lim(skb, skb_network_header(skb)); | 1095 | offset = ip6_tnl_parse_tlv_enc_lim(skb, skb_network_header(skb)); |
1089 | if (offset > 0) { | 1096 | if (offset > 0) { |
1090 | struct ipv6_tlv_tnl_enc_lim *tel; | 1097 | struct ipv6_tlv_tnl_enc_lim *tel; |
1091 | tel = (struct ipv6_tlv_tnl_enc_lim *)&skb_network_header(skb)[offset]; | 1098 | tel = (struct ipv6_tlv_tnl_enc_lim *)&skb_network_header(skb)[offset]; |
@@ -1152,7 +1159,7 @@ tx_err: | |||
1152 | static void ip6_tnl_link_config(struct ip6_tnl *t) | 1159 | static void ip6_tnl_link_config(struct ip6_tnl *t) |
1153 | { | 1160 | { |
1154 | struct net_device *dev = t->dev; | 1161 | struct net_device *dev = t->dev; |
1155 | struct ip6_tnl_parm *p = &t->parms; | 1162 | struct __ip6_tnl_parm *p = &t->parms; |
1156 | struct flowi6 *fl6 = &t->fl.u.ip6; | 1163 | struct flowi6 *fl6 = &t->fl.u.ip6; |
1157 | 1164 | ||
1158 | memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr)); | 1165 | memcpy(dev->dev_addr, &p->laddr, sizeof(struct in6_addr)); |
@@ -1215,7 +1222,7 @@ static void ip6_tnl_link_config(struct ip6_tnl *t) | |||
1215 | **/ | 1222 | **/ |
1216 | 1223 | ||
1217 | static int | 1224 | static int |
1218 | ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p) | 1225 | ip6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p) |
1219 | { | 1226 | { |
1220 | t->parms.laddr = p->laddr; | 1227 | t->parms.laddr = p->laddr; |
1221 | t->parms.raddr = p->raddr; | 1228 | t->parms.raddr = p->raddr; |
@@ -1230,6 +1237,34 @@ ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p) | |||
1230 | return 0; | 1237 | return 0; |
1231 | } | 1238 | } |
1232 | 1239 | ||
1240 | static void | ||
1241 | ip6_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm *u) | ||
1242 | { | ||
1243 | p->laddr = u->laddr; | ||
1244 | p->raddr = u->raddr; | ||
1245 | p->flags = u->flags; | ||
1246 | p->hop_limit = u->hop_limit; | ||
1247 | p->encap_limit = u->encap_limit; | ||
1248 | p->flowinfo = u->flowinfo; | ||
1249 | p->link = u->link; | ||
1250 | p->proto = u->proto; | ||
1251 | memcpy(p->name, u->name, sizeof(u->name)); | ||
1252 | } | ||
1253 | |||
1254 | static void | ||
1255 | ip6_tnl_parm_to_user(struct ip6_tnl_parm *u, const struct __ip6_tnl_parm *p) | ||
1256 | { | ||
1257 | u->laddr = p->laddr; | ||
1258 | u->raddr = p->raddr; | ||
1259 | u->flags = p->flags; | ||
1260 | u->hop_limit = p->hop_limit; | ||
1261 | u->encap_limit = p->encap_limit; | ||
1262 | u->flowinfo = p->flowinfo; | ||
1263 | u->link = p->link; | ||
1264 | u->proto = p->proto; | ||
1265 | memcpy(u->name, p->name, sizeof(u->name)); | ||
1266 | } | ||
1267 | |||
1233 | /** | 1268 | /** |
1234 | * ip6_tnl_ioctl - configure ipv6 tunnels from userspace | 1269 | * ip6_tnl_ioctl - configure ipv6 tunnels from userspace |
1235 | * @dev: virtual device associated with tunnel | 1270 | * @dev: virtual device associated with tunnel |
@@ -1263,6 +1298,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | |||
1263 | { | 1298 | { |
1264 | int err = 0; | 1299 | int err = 0; |
1265 | struct ip6_tnl_parm p; | 1300 | struct ip6_tnl_parm p; |
1301 | struct __ip6_tnl_parm p1; | ||
1266 | struct ip6_tnl *t = NULL; | 1302 | struct ip6_tnl *t = NULL; |
1267 | struct net *net = dev_net(dev); | 1303 | struct net *net = dev_net(dev); |
1268 | struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); | 1304 | struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); |
@@ -1274,11 +1310,12 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | |||
1274 | err = -EFAULT; | 1310 | err = -EFAULT; |
1275 | break; | 1311 | break; |
1276 | } | 1312 | } |
1277 | t = ip6_tnl_locate(net, &p, 0); | 1313 | ip6_tnl_parm_from_user(&p1, &p); |
1314 | t = ip6_tnl_locate(net, &p1, 0); | ||
1278 | } | 1315 | } |
1279 | if (t == NULL) | 1316 | if (t == NULL) |
1280 | t = netdev_priv(dev); | 1317 | t = netdev_priv(dev); |
1281 | memcpy(&p, &t->parms, sizeof (p)); | 1318 | ip6_tnl_parm_to_user(&p, &t->parms); |
1282 | if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) { | 1319 | if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) { |
1283 | err = -EFAULT; | 1320 | err = -EFAULT; |
1284 | } | 1321 | } |
@@ -1295,7 +1332,8 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | |||
1295 | if (p.proto != IPPROTO_IPV6 && p.proto != IPPROTO_IPIP && | 1332 | if (p.proto != IPPROTO_IPV6 && p.proto != IPPROTO_IPIP && |
1296 | p.proto != 0) | 1333 | p.proto != 0) |
1297 | break; | 1334 | break; |
1298 | t = ip6_tnl_locate(net, &p, cmd == SIOCADDTUNNEL); | 1335 | ip6_tnl_parm_from_user(&p1, &p); |
1336 | t = ip6_tnl_locate(net, &p1, cmd == SIOCADDTUNNEL); | ||
1299 | if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) { | 1337 | if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) { |
1300 | if (t != NULL) { | 1338 | if (t != NULL) { |
1301 | if (t->dev != dev) { | 1339 | if (t->dev != dev) { |
@@ -1307,13 +1345,14 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | |||
1307 | 1345 | ||
1308 | ip6_tnl_unlink(ip6n, t); | 1346 | ip6_tnl_unlink(ip6n, t); |
1309 | synchronize_net(); | 1347 | synchronize_net(); |
1310 | err = ip6_tnl_change(t, &p); | 1348 | err = ip6_tnl_change(t, &p1); |
1311 | ip6_tnl_link(ip6n, t); | 1349 | ip6_tnl_link(ip6n, t); |
1312 | netdev_state_change(dev); | 1350 | netdev_state_change(dev); |
1313 | } | 1351 | } |
1314 | if (t) { | 1352 | if (t) { |
1315 | err = 0; | 1353 | err = 0; |
1316 | if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof (p))) | 1354 | ip6_tnl_parm_to_user(&p, &t->parms); |
1355 | if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof(p))) | ||
1317 | err = -EFAULT; | 1356 | err = -EFAULT; |
1318 | 1357 | ||
1319 | } else | 1358 | } else |
@@ -1329,7 +1368,9 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | |||
1329 | if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) | 1368 | if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) |
1330 | break; | 1369 | break; |
1331 | err = -ENOENT; | 1370 | err = -ENOENT; |
1332 | if ((t = ip6_tnl_locate(net, &p, 0)) == NULL) | 1371 | ip6_tnl_parm_from_user(&p1, &p); |
1372 | t = ip6_tnl_locate(net, &p1, 0); | ||
1373 | if (t == NULL) | ||
1333 | break; | 1374 | break; |
1334 | err = -EPERM; | 1375 | err = -EPERM; |
1335 | if (t->dev == ip6n->fb_tnl_dev) | 1376 | if (t->dev == ip6n->fb_tnl_dev) |