aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/sit.c
diff options
context:
space:
mode:
authorNicolas Dichtel <nicolas.dichtel@6wind.com>2012-11-14 00:14:07 -0500
committerDavid S. Miller <davem@davemloft.net>2012-11-14 22:02:39 -0500
commitf37234160233561f2a2e3332272ae5b3725b620b (patch)
tree08e1ad16d28cc5a68b171ff57f2a6cce10a716f4 /net/ipv6/sit.c
parente4c94a9cdc5041cddaf1218397e4576a2d534f0f (diff)
sit: add support of link creation via rtnl
This patch add the support of 'ip link .. type sit'. Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/sit.c')
-rw-r--r--net/ipv6/sit.c178
1 files changed, 150 insertions, 28 deletions
diff --git a/net/ipv6/sit.c b/net/ipv6/sit.c
index 17442dec06de..7bd2a061e511 100644
--- a/net/ipv6/sit.c
+++ b/net/ipv6/sit.c
@@ -216,6 +216,37 @@ static void ipip6_tunnel_clone_6rd(struct net_device *dev, struct sit_net *sitn)
216#endif 216#endif
217} 217}
218 218
219static int ipip6_tunnel_create(struct net_device *dev)
220{
221 struct ip_tunnel *t = netdev_priv(dev);
222 struct net *net = dev_net(dev);
223 struct sit_net *sitn = net_generic(net, sit_net_id);
224 int err;
225
226 err = ipip6_tunnel_init(dev);
227 if (err < 0)
228 goto out;
229 ipip6_tunnel_clone_6rd(dev, sitn);
230
231 if (t->parms.i_flags & SIT_ISATAP)
232 dev->priv_flags |= IFF_ISATAP;
233
234 err = register_netdevice(dev);
235 if (err < 0)
236 goto out;
237
238 strcpy(t->parms.name, dev->name);
239 dev->rtnl_link_ops = &sit_link_ops;
240
241 dev_hold(dev);
242
243 ipip6_tunnel_link(sitn, t);
244 return 0;
245
246out:
247 return err;
248}
249
219static struct ip_tunnel *ipip6_tunnel_locate(struct net *net, 250static struct ip_tunnel *ipip6_tunnel_locate(struct net *net,
220 struct ip_tunnel_parm *parms, int create) 251 struct ip_tunnel_parm *parms, int create)
221{ 252{
@@ -256,22 +287,9 @@ static struct ip_tunnel *ipip6_tunnel_locate(struct net *net,
256 nt = netdev_priv(dev); 287 nt = netdev_priv(dev);
257 288
258 nt->parms = *parms; 289 nt->parms = *parms;
259 if (ipip6_tunnel_init(dev) < 0) 290 if (ipip6_tunnel_create(dev) < 0)
260 goto failed_free;
261 ipip6_tunnel_clone_6rd(dev, sitn);
262
263 if (parms->i_flags & SIT_ISATAP)
264 dev->priv_flags |= IFF_ISATAP;
265
266 if (register_netdevice(dev) < 0)
267 goto failed_free; 291 goto failed_free;
268 292
269 strcpy(nt->parms.name, dev->name);
270 dev->rtnl_link_ops = &sit_link_ops;
271
272 dev_hold(dev);
273
274 ipip6_tunnel_link(sitn, nt);
275 return nt; 293 return nt;
276 294
277failed_free: 295failed_free:
@@ -897,6 +915,27 @@ static void ipip6_tunnel_bind_dev(struct net_device *dev)
897 dev->iflink = tunnel->parms.link; 915 dev->iflink = tunnel->parms.link;
898} 916}
899 917
918static void ipip6_tunnel_update(struct ip_tunnel *t, struct ip_tunnel_parm *p)
919{
920 struct net *net = dev_net(t->dev);
921 struct sit_net *sitn = net_generic(net, sit_net_id);
922
923 ipip6_tunnel_unlink(sitn, t);
924 synchronize_net();
925 t->parms.iph.saddr = p->iph.saddr;
926 t->parms.iph.daddr = p->iph.daddr;
927 memcpy(t->dev->dev_addr, &p->iph.saddr, 4);
928 memcpy(t->dev->broadcast, &p->iph.daddr, 4);
929 ipip6_tunnel_link(sitn, t);
930 t->parms.iph.ttl = p->iph.ttl;
931 t->parms.iph.tos = p->iph.tos;
932 if (t->parms.link != p->link) {
933 t->parms.link = p->link;
934 ipip6_tunnel_bind_dev(t->dev);
935 }
936 netdev_state_change(t->dev);
937}
938
900static int 939static int
901ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) 940ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
902{ 941{
@@ -980,20 +1019,7 @@ ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd)
980 t = netdev_priv(dev); 1019 t = netdev_priv(dev);
981 } 1020 }
982 1021
983 ipip6_tunnel_unlink(sitn, t); 1022 ipip6_tunnel_update(t, &p);
984 synchronize_net();
985 t->parms.iph.saddr = p.iph.saddr;
986 t->parms.iph.daddr = p.iph.daddr;
987 memcpy(dev->dev_addr, &p.iph.saddr, 4);
988 memcpy(dev->broadcast, &p.iph.daddr, 4);
989 ipip6_tunnel_link(sitn, t);
990 t->parms.iph.ttl = p.iph.ttl;
991 t->parms.iph.tos = p.iph.tos;
992 if (t->parms.link != p.link) {
993 t->parms.link = p.link;
994 ipip6_tunnel_bind_dev(dev);
995 }
996 netdev_state_change(dev);
997 } 1023 }
998 1024
999 if (t) { 1025 if (t) {
@@ -1197,6 +1223,88 @@ static int __net_init ipip6_fb_tunnel_init(struct net_device *dev)
1197 return 0; 1223 return 0;
1198} 1224}
1199 1225
1226static void ipip6_netlink_parms(struct nlattr *data[],
1227 struct ip_tunnel_parm *parms)
1228{
1229 memset(parms, 0, sizeof(*parms));
1230
1231 parms->iph.version = 4;
1232 parms->iph.protocol = IPPROTO_IPV6;
1233 parms->iph.ihl = 5;
1234 parms->iph.ttl = 64;
1235
1236 if (!data)
1237 return;
1238
1239 if (data[IFLA_IPTUN_LINK])
1240 parms->link = nla_get_u32(data[IFLA_IPTUN_LINK]);
1241
1242 if (data[IFLA_IPTUN_LOCAL])
1243 parms->iph.saddr = nla_get_u32(data[IFLA_IPTUN_LOCAL]);
1244
1245 if (data[IFLA_IPTUN_REMOTE])
1246 parms->iph.daddr = nla_get_u32(data[IFLA_IPTUN_REMOTE]);
1247
1248 if (data[IFLA_IPTUN_TTL]) {
1249 parms->iph.ttl = nla_get_u8(data[IFLA_IPTUN_TTL]);
1250 if (parms->iph.ttl)
1251 parms->iph.frag_off = htons(IP_DF);
1252 }
1253
1254 if (data[IFLA_IPTUN_TOS])
1255 parms->iph.tos = nla_get_u8(data[IFLA_IPTUN_TOS]);
1256
1257 if (!data[IFLA_IPTUN_PMTUDISC] || nla_get_u8(data[IFLA_IPTUN_PMTUDISC]))
1258 parms->iph.frag_off = htons(IP_DF);
1259
1260 if (data[IFLA_IPTUN_FLAGS])
1261 parms->i_flags = nla_get_u16(data[IFLA_IPTUN_FLAGS]);
1262}
1263
1264static int ipip6_newlink(struct net *src_net, struct net_device *dev,
1265 struct nlattr *tb[], struct nlattr *data[])
1266{
1267 struct net *net = dev_net(dev);
1268 struct ip_tunnel *nt;
1269
1270 nt = netdev_priv(dev);
1271 ipip6_netlink_parms(data, &nt->parms);
1272
1273 if (ipip6_tunnel_locate(net, &nt->parms, 0))
1274 return -EEXIST;
1275
1276 return ipip6_tunnel_create(dev);
1277}
1278
1279static int ipip6_changelink(struct net_device *dev, struct nlattr *tb[],
1280 struct nlattr *data[])
1281{
1282 struct ip_tunnel *t;
1283 struct ip_tunnel_parm p;
1284 struct net *net = dev_net(dev);
1285 struct sit_net *sitn = net_generic(net, sit_net_id);
1286
1287 if (dev == sitn->fb_tunnel_dev)
1288 return -EINVAL;
1289
1290 ipip6_netlink_parms(data, &p);
1291
1292 if (((dev->flags & IFF_POINTOPOINT) && !p.iph.daddr) ||
1293 (!(dev->flags & IFF_POINTOPOINT) && p.iph.daddr))
1294 return -EINVAL;
1295
1296 t = ipip6_tunnel_locate(net, &p, 0);
1297
1298 if (t) {
1299 if (t->dev != dev)
1300 return -EEXIST;
1301 } else
1302 t = netdev_priv(dev);
1303
1304 ipip6_tunnel_update(t, &p);
1305 return 0;
1306}
1307
1200static size_t ipip6_get_size(const struct net_device *dev) 1308static size_t ipip6_get_size(const struct net_device *dev)
1201{ 1309{
1202 return 1310 return
@@ -1237,10 +1345,24 @@ nla_put_failure:
1237 return -EMSGSIZE; 1345 return -EMSGSIZE;
1238} 1346}
1239 1347
1348static const struct nla_policy ipip6_policy[IFLA_IPTUN_MAX + 1] = {
1349 [IFLA_IPTUN_LINK] = { .type = NLA_U32 },
1350 [IFLA_IPTUN_LOCAL] = { .type = NLA_U32 },
1351 [IFLA_IPTUN_REMOTE] = { .type = NLA_U32 },
1352 [IFLA_IPTUN_TTL] = { .type = NLA_U8 },
1353 [IFLA_IPTUN_TOS] = { .type = NLA_U8 },
1354 [IFLA_IPTUN_PMTUDISC] = { .type = NLA_U8 },
1355 [IFLA_IPTUN_FLAGS] = { .type = NLA_U16 },
1356};
1357
1240static struct rtnl_link_ops sit_link_ops __read_mostly = { 1358static struct rtnl_link_ops sit_link_ops __read_mostly = {
1241 .kind = "sit", 1359 .kind = "sit",
1242 .maxtype = IFLA_IPTUN_MAX, 1360 .maxtype = IFLA_IPTUN_MAX,
1361 .policy = ipip6_policy,
1243 .priv_size = sizeof(struct ip_tunnel), 1362 .priv_size = sizeof(struct ip_tunnel),
1363 .setup = ipip6_tunnel_setup,
1364 .newlink = ipip6_newlink,
1365 .changelink = ipip6_changelink,
1244 .get_size = ipip6_get_size, 1366 .get_size = ipip6_get_size,
1245 .fill_info = ipip6_fill_info, 1367 .fill_info = ipip6_fill_info,
1246}; 1368};