diff options
author | Nicolas Dichtel <nicolas.dichtel@6wind.com> | 2012-11-14 00:14:07 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-11-14 22:02:39 -0500 |
commit | f37234160233561f2a2e3332272ae5b3725b620b (patch) | |
tree | 08e1ad16d28cc5a68b171ff57f2a6cce10a716f4 /net/ipv6 | |
parent | e4c94a9cdc5041cddaf1218397e4576a2d534f0f (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')
-rw-r--r-- | net/ipv6/sit.c | 178 |
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 | ||
219 | static 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 | |||
246 | out: | ||
247 | return err; | ||
248 | } | ||
249 | |||
219 | static struct ip_tunnel *ipip6_tunnel_locate(struct net *net, | 250 | static 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 | ||
277 | failed_free: | 295 | failed_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 | ||
918 | static 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 | |||
900 | static int | 939 | static int |
901 | ipip6_tunnel_ioctl (struct net_device *dev, struct ifreq *ifr, int cmd) | 940 | ipip6_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 | ||
1226 | static 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 | |||
1264 | static 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 | |||
1279 | static 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 | |||
1200 | static size_t ipip6_get_size(const struct net_device *dev) | 1308 | static 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 | ||
1348 | static 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 | |||
1240 | static struct rtnl_link_ops sit_link_ops __read_mostly = { | 1358 | static 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 | }; |