diff options
Diffstat (limited to 'net/ipv6/sit.c')
-rw-r--r-- | net/ipv6/sit.c | 96 |
1 files changed, 87 insertions, 9 deletions
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c index 84c1ed246afb..08a483a8de50 100644 --- a/net/ipv6/sit.c +++ b/net/ipv6/sit.c | |||
@@ -203,12 +203,73 @@ __ipip6_tunnel_locate_prl(struct ip_tunnel *t, __be32 addr) | |||
203 | struct ip_tunnel_prl_entry *p = (struct ip_tunnel_prl_entry *)NULL; | 203 | struct ip_tunnel_prl_entry *p = (struct ip_tunnel_prl_entry *)NULL; |
204 | 204 | ||
205 | for (p = t->prl; p; p = p->next) | 205 | for (p = t->prl; p; p = p->next) |
206 | if (p->entry.addr == addr) | 206 | if (p->addr == addr) |
207 | break; | 207 | break; |
208 | return p; | 208 | return p; |
209 | 209 | ||
210 | } | 210 | } |
211 | 211 | ||
212 | static int ipip6_tunnel_get_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a) | ||
213 | { | ||
214 | struct ip_tunnel_prl *kp; | ||
215 | struct ip_tunnel_prl_entry *prl; | ||
216 | unsigned int cmax, c = 0, ca, len; | ||
217 | int ret = 0; | ||
218 | |||
219 | cmax = a->datalen / sizeof(*a); | ||
220 | if (cmax > 1 && a->addr != htonl(INADDR_ANY)) | ||
221 | cmax = 1; | ||
222 | |||
223 | /* For simple GET or for root users, | ||
224 | * we try harder to allocate. | ||
225 | */ | ||
226 | kp = (cmax <= 1 || capable(CAP_NET_ADMIN)) ? | ||
227 | kcalloc(cmax, sizeof(*kp), GFP_KERNEL) : | ||
228 | NULL; | ||
229 | |||
230 | read_lock(&ipip6_lock); | ||
231 | |||
232 | ca = t->prl_count < cmax ? t->prl_count : cmax; | ||
233 | |||
234 | if (!kp) { | ||
235 | /* We don't try hard to allocate much memory for | ||
236 | * non-root users. | ||
237 | * For root users, retry allocating enough memory for | ||
238 | * the answer. | ||
239 | */ | ||
240 | kp = kcalloc(ca, sizeof(*kp), GFP_ATOMIC); | ||
241 | if (!kp) { | ||
242 | ret = -ENOMEM; | ||
243 | goto out; | ||
244 | } | ||
245 | } | ||
246 | |||
247 | c = 0; | ||
248 | for (prl = t->prl; prl; prl = prl->next) { | ||
249 | if (c > cmax) | ||
250 | break; | ||
251 | if (a->addr != htonl(INADDR_ANY) && prl->addr != a->addr) | ||
252 | continue; | ||
253 | kp[c].addr = prl->addr; | ||
254 | kp[c].flags = prl->flags; | ||
255 | c++; | ||
256 | if (a->addr != htonl(INADDR_ANY)) | ||
257 | break; | ||
258 | } | ||
259 | out: | ||
260 | read_unlock(&ipip6_lock); | ||
261 | |||
262 | len = sizeof(*kp) * c; | ||
263 | ret = len ? copy_to_user(a->data, kp, len) : 0; | ||
264 | |||
265 | kfree(kp); | ||
266 | if (ret) | ||
267 | return -EFAULT; | ||
268 | |||
269 | a->datalen = len; | ||
270 | return 0; | ||
271 | } | ||
272 | |||
212 | static int | 273 | static int |
213 | ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg) | 274 | ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg) |
214 | { | 275 | { |
@@ -221,7 +282,7 @@ ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg) | |||
221 | write_lock(&ipip6_lock); | 282 | write_lock(&ipip6_lock); |
222 | 283 | ||
223 | for (p = t->prl; p; p = p->next) { | 284 | for (p = t->prl; p; p = p->next) { |
224 | if (p->entry.addr == a->addr) { | 285 | if (p->addr == a->addr) { |
225 | if (chg) | 286 | if (chg) |
226 | goto update; | 287 | goto update; |
227 | err = -EEXIST; | 288 | err = -EEXIST; |
@@ -242,8 +303,10 @@ ipip6_tunnel_add_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a, int chg) | |||
242 | 303 | ||
243 | p->next = t->prl; | 304 | p->next = t->prl; |
244 | t->prl = p; | 305 | t->prl = p; |
306 | t->prl_count++; | ||
245 | update: | 307 | update: |
246 | p->entry = *a; | 308 | p->addr = a->addr; |
309 | p->flags = a->flags; | ||
247 | out: | 310 | out: |
248 | write_unlock(&ipip6_lock); | 311 | write_unlock(&ipip6_lock); |
249 | return err; | 312 | return err; |
@@ -259,10 +322,11 @@ ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a) | |||
259 | 322 | ||
260 | if (a && a->addr != htonl(INADDR_ANY)) { | 323 | if (a && a->addr != htonl(INADDR_ANY)) { |
261 | for (p = &t->prl; *p; p = &(*p)->next) { | 324 | for (p = &t->prl; *p; p = &(*p)->next) { |
262 | if ((*p)->entry.addr == a->addr) { | 325 | if ((*p)->addr == a->addr) { |
263 | x = *p; | 326 | x = *p; |
264 | *p = x->next; | 327 | *p = x->next; |
265 | kfree(x); | 328 | kfree(x); |
329 | t->prl_count--; | ||
266 | goto out; | 330 | goto out; |
267 | } | 331 | } |
268 | } | 332 | } |
@@ -272,6 +336,7 @@ ipip6_tunnel_del_prl(struct ip_tunnel *t, struct ip_tunnel_prl *a) | |||
272 | x = t->prl; | 336 | x = t->prl; |
273 | t->prl = t->prl->next; | 337 | t->prl = t->prl->next; |
274 | kfree(x); | 338 | kfree(x); |
339 | t->prl_count--; | ||
275 | } | 340 | } |
276 | } | 341 | } |
277 | out: | 342 | out: |
@@ -313,7 +378,7 @@ isatap_chksrc(struct sk_buff *skb, struct iphdr *iph, struct ip_tunnel *t) | |||
313 | read_lock(&ipip6_lock); | 378 | read_lock(&ipip6_lock); |
314 | p = __ipip6_tunnel_locate_prl(t, iph->saddr); | 379 | p = __ipip6_tunnel_locate_prl(t, iph->saddr); |
315 | if (p) { | 380 | if (p) { |
316 | if (p->entry.flags & PRL_DEFAULT) | 381 | if (p->flags & PRL_DEFAULT) |
317 | skb->ndisc_nodetype = NDISC_NODETYPE_DEFAULT; | 382 | skb->ndisc_nodetype = NDISC_NODETYPE_DEFAULT; |
318 | else | 383 | else |
319 | skb->ndisc_nodetype = NDISC_NODETYPE_NODEFAULT; | 384 | skb->ndisc_nodetype = NDISC_NODETYPE_NODEFAULT; |
@@ -899,11 +964,12 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) | |||
899 | err = 0; | 964 | err = 0; |
900 | break; | 965 | break; |
901 | 966 | ||
967 | case SIOCGETPRL: | ||
902 | case SIOCADDPRL: | 968 | case SIOCADDPRL: |
903 | case SIOCDELPRL: | 969 | case SIOCDELPRL: |
904 | case SIOCCHGPRL: | 970 | case SIOCCHGPRL: |
905 | err = -EPERM; | 971 | err = -EPERM; |
906 | if (!capable(CAP_NET_ADMIN)) | 972 | if (cmd != SIOCGETPRL && !capable(CAP_NET_ADMIN)) |
907 | goto done; | 973 | goto done; |
908 | err = -EINVAL; | 974 | err = -EINVAL; |
909 | if (dev == ipip6_fb_tunnel_dev) | 975 | if (dev == ipip6_fb_tunnel_dev) |
@@ -915,11 +981,23 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) | |||
915 | if (!(t = netdev_priv(dev))) | 981 | if (!(t = netdev_priv(dev))) |
916 | goto done; | 982 | goto done; |
917 | 983 | ||
918 | if (cmd == SIOCDELPRL) | 984 | switch (cmd) { |
985 | case SIOCGETPRL: | ||
986 | err = ipip6_tunnel_get_prl(t, &prl); | ||
987 | if (!err && copy_to_user(ifr->ifr_ifru.ifru_data, | ||
988 | &prl, sizeof(prl))) | ||
989 | err = -EFAULT; | ||
990 | break; | ||
991 | case SIOCDELPRL: | ||
919 | err = ipip6_tunnel_del_prl(t, &prl); | 992 | err = ipip6_tunnel_del_prl(t, &prl); |
920 | else | 993 | break; |
994 | case SIOCADDPRL: | ||
995 | case SIOCCHGPRL: | ||
921 | err = ipip6_tunnel_add_prl(t, &prl, cmd == SIOCCHGPRL); | 996 | err = ipip6_tunnel_add_prl(t, &prl, cmd == SIOCCHGPRL); |
922 | netdev_state_change(dev); | 997 | break; |
998 | } | ||
999 | if (cmd != SIOCGETPRL) | ||
1000 | netdev_state_change(dev); | ||
923 | break; | 1001 | break; |
924 | 1002 | ||
925 | default: | 1003 | default: |