diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2008-10-09 14:59:55 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2008-10-09 14:59:55 -0400 |
commit | c19e654ddbe3831252f61e76a74d661e1a755530 (patch) | |
tree | eaeb4f5fa8c11ee6520330299f97b5ca5f305d16 /net | |
parent | 42aa916265d740d66ac1f17290366e9494c884c2 (diff) |
gre: Add netlink interface
This patch adds a netlink interface that will eventually displace
the existing ioctl interface. It utilises the elegant rtnl_link_ops
mechanism.
This also means that user-space no longer needs to rely on the
tunnel interface being of type GRE to identify GRE tunnels. The
identification can now occur using rtnl_link_ops.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/ipv4/ip_gre.c | 247 |
1 files changed, 243 insertions, 4 deletions
diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 80622dd6fb3..25d2c77a7f3 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c | |||
@@ -41,6 +41,7 @@ | |||
41 | #include <net/xfrm.h> | 41 | #include <net/xfrm.h> |
42 | #include <net/net_namespace.h> | 42 | #include <net/net_namespace.h> |
43 | #include <net/netns/generic.h> | 43 | #include <net/netns/generic.h> |
44 | #include <net/rtnetlink.h> | ||
44 | 45 | ||
45 | #ifdef CONFIG_IPV6 | 46 | #ifdef CONFIG_IPV6 |
46 | #include <net/ipv6.h> | 47 | #include <net/ipv6.h> |
@@ -117,6 +118,7 @@ | |||
117 | Alexey Kuznetsov. | 118 | Alexey Kuznetsov. |
118 | */ | 119 | */ |
119 | 120 | ||
121 | static struct rtnl_link_ops ipgre_link_ops __read_mostly; | ||
120 | static int ipgre_tunnel_init(struct net_device *dev); | 122 | static int ipgre_tunnel_init(struct net_device *dev); |
121 | static void ipgre_tunnel_setup(struct net_device *dev); | 123 | static void ipgre_tunnel_setup(struct net_device *dev); |
122 | static int ipgre_tunnel_bind_dev(struct net_device *dev); | 124 | static int ipgre_tunnel_bind_dev(struct net_device *dev); |
@@ -286,9 +288,9 @@ static struct ip_tunnel * ipgre_tunnel_locate(struct net *net, | |||
286 | goto failed_free; | 288 | goto failed_free; |
287 | } | 289 | } |
288 | 290 | ||
289 | dev->init = ipgre_tunnel_init; | ||
290 | nt = netdev_priv(dev); | 291 | nt = netdev_priv(dev); |
291 | nt->parms = *parms; | 292 | nt->parms = *parms; |
293 | dev->rtnl_link_ops = &ipgre_link_ops; | ||
292 | 294 | ||
293 | dev->mtu = ipgre_tunnel_bind_dev(dev); | 295 | dev->mtu = ipgre_tunnel_bind_dev(dev); |
294 | 296 | ||
@@ -1087,6 +1089,7 @@ static int ipgre_close(struct net_device *dev) | |||
1087 | 1089 | ||
1088 | static void ipgre_tunnel_setup(struct net_device *dev) | 1090 | static void ipgre_tunnel_setup(struct net_device *dev) |
1089 | { | 1091 | { |
1092 | dev->init = ipgre_tunnel_init; | ||
1090 | dev->uninit = ipgre_tunnel_uninit; | 1093 | dev->uninit = ipgre_tunnel_uninit; |
1091 | dev->destructor = free_netdev; | 1094 | dev->destructor = free_netdev; |
1092 | dev->hard_start_xmit = ipgre_tunnel_xmit; | 1095 | dev->hard_start_xmit = ipgre_tunnel_xmit; |
@@ -1196,6 +1199,7 @@ static int ipgre_init_net(struct net *net) | |||
1196 | 1199 | ||
1197 | ign->fb_tunnel_dev->init = ipgre_fb_tunnel_init; | 1200 | ign->fb_tunnel_dev->init = ipgre_fb_tunnel_init; |
1198 | dev_net_set(ign->fb_tunnel_dev, net); | 1201 | dev_net_set(ign->fb_tunnel_dev, net); |
1202 | ign->fb_tunnel_dev->rtnl_link_ops = &ipgre_link_ops; | ||
1199 | 1203 | ||
1200 | if ((err = register_netdev(ign->fb_tunnel_dev))) | 1204 | if ((err = register_netdev(ign->fb_tunnel_dev))) |
1201 | goto err_reg_dev; | 1205 | goto err_reg_dev; |
@@ -1228,6 +1232,229 @@ static struct pernet_operations ipgre_net_ops = { | |||
1228 | .exit = ipgre_exit_net, | 1232 | .exit = ipgre_exit_net, |
1229 | }; | 1233 | }; |
1230 | 1234 | ||
1235 | static int ipgre_tunnel_validate(struct nlattr *tb[], struct nlattr *data[]) | ||
1236 | { | ||
1237 | __be16 flags; | ||
1238 | |||
1239 | if (!data) | ||
1240 | return 0; | ||
1241 | |||
1242 | flags = 0; | ||
1243 | if (data[IFLA_GRE_IFLAGS]) | ||
1244 | flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]); | ||
1245 | if (data[IFLA_GRE_OFLAGS]) | ||
1246 | flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]); | ||
1247 | if (flags & (GRE_VERSION|GRE_ROUTING)) | ||
1248 | return -EINVAL; | ||
1249 | |||
1250 | return 0; | ||
1251 | } | ||
1252 | |||
1253 | static void ipgre_netlink_parms(struct nlattr *data[], | ||
1254 | struct ip_tunnel_parm *parms) | ||
1255 | { | ||
1256 | memset(parms, 0, sizeof(parms)); | ||
1257 | |||
1258 | parms->iph.protocol = IPPROTO_GRE; | ||
1259 | |||
1260 | if (!data) | ||
1261 | return; | ||
1262 | |||
1263 | if (data[IFLA_GRE_LINK]) | ||
1264 | parms->link = nla_get_u32(data[IFLA_GRE_LINK]); | ||
1265 | |||
1266 | if (data[IFLA_GRE_IFLAGS]) | ||
1267 | parms->i_flags = nla_get_be16(data[IFLA_GRE_IFLAGS]); | ||
1268 | |||
1269 | if (data[IFLA_GRE_OFLAGS]) | ||
1270 | parms->o_flags = nla_get_be16(data[IFLA_GRE_OFLAGS]); | ||
1271 | |||
1272 | if (data[IFLA_GRE_IKEY]) | ||
1273 | parms->i_key = nla_get_be32(data[IFLA_GRE_IKEY]); | ||
1274 | |||
1275 | if (data[IFLA_GRE_OKEY]) | ||
1276 | parms->o_key = nla_get_be32(data[IFLA_GRE_OKEY]); | ||
1277 | |||
1278 | if (data[IFLA_GRE_LOCAL]) | ||
1279 | memcpy(&parms->iph.saddr, nla_data(data[IFLA_GRE_LOCAL]), 4); | ||
1280 | |||
1281 | if (data[IFLA_GRE_REMOTE]) | ||
1282 | memcpy(&parms->iph.daddr, nla_data(data[IFLA_GRE_REMOTE]), 4); | ||
1283 | |||
1284 | if (data[IFLA_GRE_TTL]) | ||
1285 | parms->iph.ttl = nla_get_u8(data[IFLA_GRE_TTL]); | ||
1286 | |||
1287 | if (data[IFLA_GRE_TOS]) | ||
1288 | parms->iph.tos = nla_get_u8(data[IFLA_GRE_TOS]); | ||
1289 | |||
1290 | if (!data[IFLA_GRE_PMTUDISC] || nla_get_u8(data[IFLA_GRE_PMTUDISC])) | ||
1291 | parms->iph.frag_off = htons(IP_DF); | ||
1292 | } | ||
1293 | |||
1294 | static int ipgre_newlink(struct net_device *dev, struct nlattr *tb[], | ||
1295 | struct nlattr *data[]) | ||
1296 | { | ||
1297 | struct ip_tunnel *nt; | ||
1298 | struct net *net = dev_net(dev); | ||
1299 | struct ipgre_net *ign = net_generic(net, ipgre_net_id); | ||
1300 | int mtu; | ||
1301 | int err; | ||
1302 | |||
1303 | nt = netdev_priv(dev); | ||
1304 | ipgre_netlink_parms(data, &nt->parms); | ||
1305 | |||
1306 | if (ipgre_tunnel_locate(net, &nt->parms, 0)) | ||
1307 | return -EEXIST; | ||
1308 | |||
1309 | mtu = ipgre_tunnel_bind_dev(dev); | ||
1310 | if (!tb[IFLA_MTU]) | ||
1311 | dev->mtu = mtu; | ||
1312 | |||
1313 | err = register_netdevice(dev); | ||
1314 | if (err) | ||
1315 | goto out; | ||
1316 | |||
1317 | dev_hold(dev); | ||
1318 | ipgre_tunnel_link(ign, nt); | ||
1319 | |||
1320 | out: | ||
1321 | return err; | ||
1322 | } | ||
1323 | |||
1324 | static int ipgre_changelink(struct net_device *dev, struct nlattr *tb[], | ||
1325 | struct nlattr *data[]) | ||
1326 | { | ||
1327 | struct ip_tunnel *t, *nt; | ||
1328 | struct net *net = dev_net(dev); | ||
1329 | struct ipgre_net *ign = net_generic(net, ipgre_net_id); | ||
1330 | struct ip_tunnel_parm p; | ||
1331 | int mtu; | ||
1332 | |||
1333 | if (dev == ign->fb_tunnel_dev) | ||
1334 | return -EINVAL; | ||
1335 | |||
1336 | nt = netdev_priv(dev); | ||
1337 | ipgre_netlink_parms(data, &p); | ||
1338 | |||
1339 | t = ipgre_tunnel_locate(net, &p, 0); | ||
1340 | |||
1341 | if (t) { | ||
1342 | if (t->dev != dev) | ||
1343 | return -EEXIST; | ||
1344 | } else { | ||
1345 | unsigned nflags = 0; | ||
1346 | |||
1347 | t = nt; | ||
1348 | |||
1349 | if (ipv4_is_multicast(p.iph.daddr)) | ||
1350 | nflags = IFF_BROADCAST; | ||
1351 | else if (p.iph.daddr) | ||
1352 | nflags = IFF_POINTOPOINT; | ||
1353 | |||
1354 | if ((dev->flags ^ nflags) & | ||
1355 | (IFF_POINTOPOINT | IFF_BROADCAST)) | ||
1356 | return -EINVAL; | ||
1357 | |||
1358 | ipgre_tunnel_unlink(ign, t); | ||
1359 | t->parms.iph.saddr = p.iph.saddr; | ||
1360 | t->parms.iph.daddr = p.iph.daddr; | ||
1361 | t->parms.i_key = p.i_key; | ||
1362 | memcpy(dev->dev_addr, &p.iph.saddr, 4); | ||
1363 | memcpy(dev->broadcast, &p.iph.daddr, 4); | ||
1364 | ipgre_tunnel_link(ign, t); | ||
1365 | netdev_state_change(dev); | ||
1366 | } | ||
1367 | |||
1368 | t->parms.o_key = p.o_key; | ||
1369 | t->parms.iph.ttl = p.iph.ttl; | ||
1370 | t->parms.iph.tos = p.iph.tos; | ||
1371 | t->parms.iph.frag_off = p.iph.frag_off; | ||
1372 | |||
1373 | if (t->parms.link != p.link) { | ||
1374 | t->parms.link = p.link; | ||
1375 | mtu = ipgre_tunnel_bind_dev(dev); | ||
1376 | if (!tb[IFLA_MTU]) | ||
1377 | dev->mtu = mtu; | ||
1378 | netdev_state_change(dev); | ||
1379 | } | ||
1380 | |||
1381 | return 0; | ||
1382 | } | ||
1383 | |||
1384 | static size_t ipgre_get_size(const struct net_device *dev) | ||
1385 | { | ||
1386 | return | ||
1387 | /* IFLA_GRE_LINK */ | ||
1388 | nla_total_size(4) + | ||
1389 | /* IFLA_GRE_IFLAGS */ | ||
1390 | nla_total_size(2) + | ||
1391 | /* IFLA_GRE_OFLAGS */ | ||
1392 | nla_total_size(2) + | ||
1393 | /* IFLA_GRE_IKEY */ | ||
1394 | nla_total_size(4) + | ||
1395 | /* IFLA_GRE_OKEY */ | ||
1396 | nla_total_size(4) + | ||
1397 | /* IFLA_GRE_LOCAL */ | ||
1398 | nla_total_size(4) + | ||
1399 | /* IFLA_GRE_REMOTE */ | ||
1400 | nla_total_size(4) + | ||
1401 | /* IFLA_GRE_TTL */ | ||
1402 | nla_total_size(1) + | ||
1403 | /* IFLA_GRE_TOS */ | ||
1404 | nla_total_size(1) + | ||
1405 | /* IFLA_GRE_PMTUDISC */ | ||
1406 | nla_total_size(1) + | ||
1407 | 0; | ||
1408 | } | ||
1409 | |||
1410 | static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev) | ||
1411 | { | ||
1412 | struct ip_tunnel *t = netdev_priv(dev); | ||
1413 | struct ip_tunnel_parm *p = &t->parms; | ||
1414 | |||
1415 | NLA_PUT_U32(skb, IFLA_GRE_LINK, p->link); | ||
1416 | NLA_PUT_BE16(skb, IFLA_GRE_IFLAGS, p->i_flags); | ||
1417 | NLA_PUT_BE16(skb, IFLA_GRE_OFLAGS, p->o_flags); | ||
1418 | NLA_PUT_BE32(skb, IFLA_GRE_IFLAGS, p->i_flags); | ||
1419 | NLA_PUT_BE32(skb, IFLA_GRE_OFLAGS, p->o_flags); | ||
1420 | NLA_PUT(skb, IFLA_GRE_LOCAL, 4, &p->iph.saddr); | ||
1421 | NLA_PUT(skb, IFLA_GRE_REMOTE, 4, &p->iph.daddr); | ||
1422 | NLA_PUT_U8(skb, IFLA_GRE_TTL, p->iph.ttl); | ||
1423 | NLA_PUT_U8(skb, IFLA_GRE_TOS, p->iph.tos); | ||
1424 | NLA_PUT_U8(skb, IFLA_GRE_PMTUDISC, !!(p->iph.frag_off & htons(IP_DF))); | ||
1425 | |||
1426 | return 0; | ||
1427 | |||
1428 | nla_put_failure: | ||
1429 | return -EMSGSIZE; | ||
1430 | } | ||
1431 | |||
1432 | static const struct nla_policy ipgre_policy[IFLA_GRE_MAX + 1] = { | ||
1433 | [IFLA_GRE_LINK] = { .type = NLA_U32 }, | ||
1434 | [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, | ||
1435 | [IFLA_GRE_OFLAGS] = { .type = NLA_U16 }, | ||
1436 | [IFLA_GRE_IKEY] = { .type = NLA_U32 }, | ||
1437 | [IFLA_GRE_OKEY] = { .type = NLA_U32 }, | ||
1438 | [IFLA_GRE_LOCAL] = { .len = 4 }, | ||
1439 | [IFLA_GRE_REMOTE] = { .len = 4 }, | ||
1440 | [IFLA_GRE_TTL] = { .type = NLA_U8 }, | ||
1441 | [IFLA_GRE_TOS] = { .type = NLA_U8 }, | ||
1442 | [IFLA_GRE_PMTUDISC] = { .type = NLA_U8 }, | ||
1443 | }; | ||
1444 | |||
1445 | static struct rtnl_link_ops ipgre_link_ops __read_mostly = { | ||
1446 | .kind = "gre", | ||
1447 | .maxtype = IFLA_GRE_MAX, | ||
1448 | .policy = ipgre_policy, | ||
1449 | .priv_size = sizeof(struct ip_tunnel), | ||
1450 | .setup = ipgre_tunnel_setup, | ||
1451 | .validate = ipgre_tunnel_validate, | ||
1452 | .newlink = ipgre_newlink, | ||
1453 | .changelink = ipgre_changelink, | ||
1454 | .get_size = ipgre_get_size, | ||
1455 | .fill_info = ipgre_fill_info, | ||
1456 | }; | ||
1457 | |||
1231 | /* | 1458 | /* |
1232 | * And now the modules code and kernel interface. | 1459 | * And now the modules code and kernel interface. |
1233 | */ | 1460 | */ |
@@ -1245,19 +1472,31 @@ static int __init ipgre_init(void) | |||
1245 | 1472 | ||
1246 | err = register_pernet_gen_device(&ipgre_net_id, &ipgre_net_ops); | 1473 | err = register_pernet_gen_device(&ipgre_net_id, &ipgre_net_ops); |
1247 | if (err < 0) | 1474 | if (err < 0) |
1248 | inet_del_protocol(&ipgre_protocol, IPPROTO_GRE); | 1475 | goto gen_device_failed; |
1249 | 1476 | ||
1477 | err = rtnl_link_register(&ipgre_link_ops); | ||
1478 | if (err < 0) | ||
1479 | goto rtnl_link_failed; | ||
1480 | |||
1481 | out: | ||
1250 | return err; | 1482 | return err; |
1483 | |||
1484 | rtnl_link_failed: | ||
1485 | unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops); | ||
1486 | gen_device_failed: | ||
1487 | inet_del_protocol(&ipgre_protocol, IPPROTO_GRE); | ||
1488 | goto out; | ||
1251 | } | 1489 | } |
1252 | 1490 | ||
1253 | static void __exit ipgre_fini(void) | 1491 | static void __exit ipgre_fini(void) |
1254 | { | 1492 | { |
1493 | rtnl_link_unregister(&ipgre_link_ops); | ||
1494 | unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops); | ||
1255 | if (inet_del_protocol(&ipgre_protocol, IPPROTO_GRE) < 0) | 1495 | if (inet_del_protocol(&ipgre_protocol, IPPROTO_GRE) < 0) |
1256 | printk(KERN_INFO "ipgre close: can't remove protocol\n"); | 1496 | printk(KERN_INFO "ipgre close: can't remove protocol\n"); |
1257 | |||
1258 | unregister_pernet_gen_device(ipgre_net_id, &ipgre_net_ops); | ||
1259 | } | 1497 | } |
1260 | 1498 | ||
1261 | module_init(ipgre_init); | 1499 | module_init(ipgre_init); |
1262 | module_exit(ipgre_fini); | 1500 | module_exit(ipgre_fini); |
1263 | MODULE_LICENSE("GPL"); | 1501 | MODULE_LICENSE("GPL"); |
1502 | MODULE_ALIAS("rtnl-link-gre"); | ||