| /* |
| * lib/route/link/veth.c Virtual Ethernet |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation version 2.1 |
| * of the License. |
| * |
| * Copyright (c) 2013 Cong Wang <xiyou.wangcong@gmail.com> |
| */ |
| |
| /** |
| * @ingroup link |
| * @defgroup veth VETH |
| * Virtual Ethernet |
| * |
| * @details |
| * \b Link Type Name: "veth" |
| * |
| * @route_doc{link_veth, VETH Documentation} |
| * |
| * @{ |
| */ |
| |
| #include <netlink-private/netlink.h> |
| #include <netlink/netlink.h> |
| #include <netlink/attr.h> |
| #include <netlink/utils.h> |
| #include <netlink/object.h> |
| #include <netlink/route/rtnl.h> |
| #include <netlink-private/route/link/api.h> |
| #include <netlink/route/link/veth.h> |
| |
| #include <linux/if_link.h> |
| |
| static struct nla_policy veth_policy[VETH_INFO_MAX+1] = { |
| [VETH_INFO_PEER] = { .minlen = sizeof(struct ifinfomsg) }, |
| }; |
| |
| static int veth_parse(struct rtnl_link *link, struct nlattr *data, |
| struct nlattr *xstats) |
| { |
| struct nlattr *tb[VETH_INFO_MAX+1]; |
| struct nlattr *peer_tb[IFLA_MAX + 1]; |
| struct rtnl_link *peer = link->l_info; |
| int err; |
| |
| NL_DBG(3, "Parsing veth link info"); |
| |
| if ((err = nla_parse_nested(tb, VETH_INFO_MAX, data, veth_policy)) < 0) |
| goto errout; |
| |
| if (tb[VETH_INFO_PEER]) { |
| struct nlattr *nla_peer; |
| struct ifinfomsg *ifi; |
| |
| nla_peer = tb[VETH_INFO_PEER]; |
| ifi = nla_data(nla_peer); |
| |
| peer->l_family = ifi->ifi_family; |
| peer->l_arptype = ifi->ifi_type; |
| peer->l_index = ifi->ifi_index; |
| peer->l_flags = ifi->ifi_flags; |
| peer->l_change = ifi->ifi_change; |
| err = nla_parse(peer_tb, IFLA_MAX, |
| nla_data(nla_peer) + sizeof(struct ifinfomsg), |
| nla_len(nla_peer) - sizeof(struct ifinfomsg), |
| rtln_link_policy); |
| if (err < 0) |
| goto errout; |
| |
| err = rtnl_link_info_parse(peer, peer_tb); |
| if (err < 0) |
| goto errout; |
| } |
| |
| err = 0; |
| |
| errout: |
| return err; |
| } |
| |
| static void veth_dump_line(struct rtnl_link *link, struct nl_dump_params *p) |
| { |
| } |
| |
| static void veth_dump_details(struct rtnl_link *link, struct nl_dump_params *p) |
| { |
| struct rtnl_link *peer = link->l_info; |
| char *name; |
| name = rtnl_link_get_name(peer); |
| nl_dump(p, " peer "); |
| if (name) |
| nl_dump_line(p, "%s\n", name); |
| else |
| nl_dump_line(p, "%u\n", peer->l_index); |
| } |
| |
| static int veth_clone(struct rtnl_link *dst, struct rtnl_link *src) |
| { |
| struct rtnl_link *dst_peer = NULL, *src_peer = src->l_info; |
| |
| /* we are calling nl_object_clone() recursively, this should |
| * happen only once */ |
| if (src_peer) { |
| src_peer->l_info = NULL; |
| dst_peer = (struct rtnl_link *)nl_object_clone(OBJ_CAST(src_peer)); |
| if (!dst_peer) |
| return -NLE_NOMEM; |
| src_peer->l_info = src; |
| dst_peer->l_info = dst; |
| } |
| dst->l_info = dst_peer; |
| return 0; |
| } |
| |
| static int veth_put_attrs(struct nl_msg *msg, struct rtnl_link *link) |
| { |
| struct rtnl_link *peer = link->l_info; |
| struct ifinfomsg ifi; |
| struct nlattr *data, *info_peer; |
| |
| memset(&ifi, 0, sizeof ifi); |
| ifi.ifi_family = peer->l_family; |
| ifi.ifi_type = peer->l_arptype; |
| ifi.ifi_index = peer->l_index; |
| ifi.ifi_flags = peer->l_flags; |
| ifi.ifi_change = peer->l_change; |
| |
| if (!(data = nla_nest_start(msg, IFLA_INFO_DATA))) |
| return -NLE_MSGSIZE; |
| if (!(info_peer = nla_nest_start(msg, VETH_INFO_PEER))) |
| return -NLE_MSGSIZE; |
| if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0) |
| return -NLE_MSGSIZE; |
| rtnl_link_fill_info(msg, peer); |
| nla_nest_end(msg, info_peer); |
| nla_nest_end(msg, data); |
| |
| return 0; |
| } |
| |
| static int veth_alloc(struct rtnl_link *link) |
| { |
| struct rtnl_link *peer; |
| int err; |
| |
| /* return early if we are in recursion */ |
| if (link->l_info) |
| return 0; |
| |
| if (!(peer = rtnl_link_alloc())) |
| return -NLE_NOMEM; |
| |
| /* We don't need to hold a reference here, as link and |
| * its peer should always be freed together. |
| */ |
| peer->l_info = link; |
| if ((err = rtnl_link_set_type(peer, "veth")) < 0) { |
| rtnl_link_put(peer); |
| return err; |
| } |
| |
| link->l_info = peer; |
| return 0; |
| } |
| |
| static void veth_free(struct rtnl_link *link) |
| { |
| struct rtnl_link *peer = link->l_info; |
| if (peer) { |
| link->l_info = NULL; |
| /* avoid calling this recursively */ |
| peer->l_info = NULL; |
| rtnl_link_put(peer); |
| } |
| /* the caller should finally free link */ |
| } |
| |
| static struct rtnl_link_info_ops veth_info_ops = { |
| .io_name = "veth", |
| .io_parse = veth_parse, |
| .io_dump = { |
| [NL_DUMP_LINE] = veth_dump_line, |
| [NL_DUMP_DETAILS] = veth_dump_details, |
| }, |
| .io_alloc = veth_alloc, |
| .io_clone = veth_clone, |
| .io_put_attrs = veth_put_attrs, |
| .io_free = veth_free, |
| }; |
| |
| /** @cond SKIP */ |
| |
| #define IS_VETH_LINK_ASSERT(link) \ |
| if ((link)->l_info_ops != &veth_info_ops) { \ |
| APPBUG("Link is not a veth link. set type \"veth\" first."); \ |
| return NULL; \ |
| } |
| /** @endcond */ |
| |
| /** |
| * @name VETH Object |
| * @{ |
| */ |
| |
| /** |
| * Allocate link object of type veth |
| * |
| * @return Allocated link object or NULL. |
| */ |
| struct rtnl_link *rtnl_link_veth_alloc(void) |
| { |
| struct rtnl_link *link; |
| int err; |
| |
| if (!(link = rtnl_link_alloc())) |
| return NULL; |
| if ((err = rtnl_link_set_type(link, "veth")) < 0) { |
| rtnl_link_put(link); |
| return NULL; |
| } |
| |
| return link; |
| } |
| |
| /** |
| * Get the peer link of a veth link |
| * |
| * @return the peer link object. |
| */ |
| struct rtnl_link *rtnl_link_veth_get_peer(struct rtnl_link *link) |
| { |
| IS_VETH_LINK_ASSERT(link); |
| nl_object_get(OBJ_CAST(link->l_info)); |
| return link->l_info; |
| } |
| |
| /** |
| * Release a veth link and its peer |
| * |
| */ |
| void rtnl_link_veth_release(struct rtnl_link *link) |
| { |
| veth_free(link); |
| rtnl_link_put(link); |
| } |
| |
| /** |
| * Check if link is a veth link |
| * @arg link Link object |
| * |
| * @return True if link is a veth link, otherwise false is returned. |
| */ |
| int rtnl_link_is_veth(struct rtnl_link *link) |
| { |
| return link->l_info_ops && !strcmp(link->l_info_ops->io_name, "veth"); |
| } |
| |
| /** |
| * Create a new kernel veth device |
| * @arg sock netlink socket |
| * @arg name name of the veth device or NULL |
| * @arg peer_name name of its peer or NULL |
| * @arg pid pid of the process in the new netns |
| * |
| * Creates a new veth device pair in the kernel and move the peer |
| * to the network namespace where the process is. If no name is |
| * provided, the kernel will automatically pick a name of the |
| * form "veth%d" (e.g. veth0, veth1, etc.) |
| * |
| * @return 0 on success or a negative error code |
| */ |
| int rtnl_link_veth_add(struct nl_sock *sock, const char *name, |
| const char *peer_name, pid_t pid) |
| { |
| struct rtnl_link *link, *peer; |
| int err = -NLE_NOMEM; |
| |
| if (!(link = rtnl_link_veth_alloc())) |
| return -NLE_NOMEM; |
| peer = link->l_info; |
| |
| if (name && peer_name) { |
| rtnl_link_set_name(link, name); |
| rtnl_link_set_name(peer, peer_name); |
| } |
| |
| rtnl_link_set_ns_pid(peer, pid); |
| err = rtnl_link_add(sock, link, NLM_F_CREATE | NLM_F_EXCL); |
| |
| rtnl_link_put(link); |
| return err; |
| } |
| |
| /** @} */ |
| |
| static void __init veth_init(void) |
| { |
| rtnl_link_register_info(&veth_info_ops); |
| } |
| |
| static void __exit veth_exit(void) |
| { |
| rtnl_link_unregister_info(&veth_info_ops); |
| } |
| |
| /** @} */ |