Improve rtnl_link_change() behaviour
- avoid unncessary name change requests The kernel does not check if the specified IFNAME is different from the current name. It assumes that if IFNAME and ifindex are both specified, a name change is requested. Therefore avoid specyfing IFNAME if ifindex is provided and original and new name are identical. - move link building to own function (to allow link add later on) - error if immutable changes have been made - better documentation
This commit is contained in:
parent
b5918b5ce3
commit
96bc6d6f66
3 changed files with 132 additions and 75 deletions
|
@ -48,8 +48,9 @@ extern "C" {
|
|||
#define NLE_PKTLOC_FILE 29
|
||||
#define NLE_PARSE_ERR 30
|
||||
#define NLE_NODEV 31
|
||||
#define NLE_IMMUTABLE 32
|
||||
|
||||
#define NLE_MAX NLE_NODEV
|
||||
#define NLE_MAX NLE_IMMUTABLE
|
||||
|
||||
extern const char * nl_geterror(int);
|
||||
extern void nl_perror(int, const char *);
|
||||
|
|
|
@ -45,6 +45,7 @@ static const char *errmsg[NLE_MAX+1] = {
|
|||
[NLE_PKTLOC_FILE] = "Unable to open packet location file",
|
||||
[NLE_PARSE_ERR] = "Unable to parse object",
|
||||
[NLE_NODEV] = "No such device",
|
||||
[NLE_IMMUTABLE] = "Immutable attribute",
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
203
lib/route/link.c
203
lib/route/link.c
|
@ -1031,85 +1031,56 @@ struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *cache,
|
|||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* Builds a netlink change request message to change link attributes
|
||||
* @arg old link to be changed
|
||||
* @arg tmpl template with requested changes
|
||||
* @arg flags additional netlink message flags
|
||||
* @arg result Result pointer
|
||||
*
|
||||
* Builds a new netlink message requesting a change of link attributes.
|
||||
* The netlink message header isn't fully equipped with all relevant
|
||||
* fields and must be sent out via nl_send_auto_complete() or
|
||||
* supplemented as needed.
|
||||
* \a old must point to a link currently configured in the kernel
|
||||
* and \a tmpl must contain the attributes to be changed set via
|
||||
* \c rtnl_link_set_* functions.
|
||||
*
|
||||
* @return 0 on success or a negative error code.
|
||||
* @note Not all attributes can be changed, see
|
||||
* \ref link_changeable "Changeable Attributes" for more details.
|
||||
*/
|
||||
int rtnl_link_build_change_request(struct rtnl_link *old,
|
||||
struct rtnl_link *tmpl, int flags,
|
||||
struct nl_msg **result)
|
||||
static int build_link_msg(int cmd, struct ifinfomsg *hdr,
|
||||
struct rtnl_link *link, int flags, struct nl_msg **result)
|
||||
{
|
||||
struct nl_msg *msg;
|
||||
struct nlattr *af_spec;
|
||||
struct ifinfomsg ifi = {
|
||||
.ifi_family = old->l_family,
|
||||
.ifi_index = old->l_index,
|
||||
};
|
||||
|
||||
if (tmpl->ce_mask & LINK_ATTR_FLAGS) {
|
||||
ifi.ifi_flags = old->l_flags & ~tmpl->l_flag_mask;
|
||||
ifi.ifi_flags |= tmpl->l_flags;
|
||||
}
|
||||
|
||||
msg = nlmsg_alloc_simple(RTM_SETLINK, flags);
|
||||
msg = nlmsg_alloc_simple(cmd, flags);
|
||||
if (!msg)
|
||||
return -NLE_NOMEM;
|
||||
|
||||
if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
|
||||
if (nlmsg_append(msg, hdr, sizeof(*hdr), NLMSG_ALIGNTO) < 0)
|
||||
goto nla_put_failure;
|
||||
|
||||
if (tmpl->ce_mask & LINK_ATTR_ADDR)
|
||||
NLA_PUT_ADDR(msg, IFLA_ADDRESS, tmpl->l_addr);
|
||||
if (link->ce_mask & LINK_ATTR_ADDR)
|
||||
NLA_PUT_ADDR(msg, IFLA_ADDRESS, link->l_addr);
|
||||
|
||||
if (tmpl->ce_mask & LINK_ATTR_BRD)
|
||||
NLA_PUT_ADDR(msg, IFLA_BROADCAST, tmpl->l_bcast);
|
||||
if (link->ce_mask & LINK_ATTR_BRD)
|
||||
NLA_PUT_ADDR(msg, IFLA_BROADCAST, link->l_bcast);
|
||||
|
||||
if (tmpl->ce_mask & LINK_ATTR_MTU)
|
||||
NLA_PUT_U32(msg, IFLA_MTU, tmpl->l_mtu);
|
||||
if (link->ce_mask & LINK_ATTR_MTU)
|
||||
NLA_PUT_U32(msg, IFLA_MTU, link->l_mtu);
|
||||
|
||||
if (tmpl->ce_mask & LINK_ATTR_TXQLEN)
|
||||
NLA_PUT_U32(msg, IFLA_TXQLEN, tmpl->l_txqlen);
|
||||
if (link->ce_mask & LINK_ATTR_TXQLEN)
|
||||
NLA_PUT_U32(msg, IFLA_TXQLEN, link->l_txqlen);
|
||||
|
||||
if (tmpl->ce_mask & LINK_ATTR_WEIGHT)
|
||||
NLA_PUT_U32(msg, IFLA_WEIGHT, tmpl->l_weight);
|
||||
if (link->ce_mask & LINK_ATTR_WEIGHT)
|
||||
NLA_PUT_U32(msg, IFLA_WEIGHT, link->l_weight);
|
||||
|
||||
if (tmpl->ce_mask & LINK_ATTR_IFNAME)
|
||||
NLA_PUT_STRING(msg, IFLA_IFNAME, tmpl->l_name);
|
||||
if (link->ce_mask & LINK_ATTR_IFNAME)
|
||||
NLA_PUT_STRING(msg, IFLA_IFNAME, link->l_name);
|
||||
|
||||
if (tmpl->ce_mask & LINK_ATTR_OPERSTATE)
|
||||
NLA_PUT_U8(msg, IFLA_OPERSTATE, tmpl->l_operstate);
|
||||
if (link->ce_mask & LINK_ATTR_OPERSTATE)
|
||||
NLA_PUT_U8(msg, IFLA_OPERSTATE, link->l_operstate);
|
||||
|
||||
if (tmpl->ce_mask & LINK_ATTR_LINKMODE)
|
||||
NLA_PUT_U8(msg, IFLA_LINKMODE, tmpl->l_linkmode);
|
||||
if (link->ce_mask & LINK_ATTR_LINKMODE)
|
||||
NLA_PUT_U8(msg, IFLA_LINKMODE, link->l_linkmode);
|
||||
|
||||
if (tmpl->ce_mask & LINK_ATTR_IFALIAS)
|
||||
NLA_PUT_STRING(msg, IFLA_IFALIAS, tmpl->l_ifalias);
|
||||
if (link->ce_mask & LINK_ATTR_IFALIAS)
|
||||
NLA_PUT_STRING(msg, IFLA_IFALIAS, link->l_ifalias);
|
||||
|
||||
if ((tmpl->ce_mask & LINK_ATTR_LINKINFO) && tmpl->l_info_ops &&
|
||||
tmpl->l_info_ops->io_put_attrs) {
|
||||
if ((link->ce_mask & LINK_ATTR_LINKINFO) && link->l_info_ops &&
|
||||
link->l_info_ops->io_put_attrs) {
|
||||
struct nlattr *info;
|
||||
|
||||
if (!(info = nla_nest_start(msg, IFLA_LINKINFO)))
|
||||
goto nla_put_failure;
|
||||
|
||||
NLA_PUT_STRING(msg, IFLA_INFO_KIND, tmpl->l_info_ops->io_name);
|
||||
NLA_PUT_STRING(msg, IFLA_INFO_KIND, link->l_info_ops->io_name);
|
||||
|
||||
if (tmpl->l_info_ops->io_put_attrs(msg, tmpl) < 0)
|
||||
if (link->l_info_ops->io_put_attrs(msg, link) < 0)
|
||||
goto nla_put_failure;
|
||||
|
||||
nla_nest_end(msg, info);
|
||||
|
@ -1118,7 +1089,7 @@ int rtnl_link_build_change_request(struct rtnl_link *old,
|
|||
if (!(af_spec = nla_nest_start(msg, IFLA_AF_SPEC)))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (do_foreach_af(tmpl, af_fill, msg) < 0)
|
||||
if (do_foreach_af(link, af_fill, msg) < 0)
|
||||
goto nla_put_failure;
|
||||
|
||||
nla_nest_end(msg, af_spec);
|
||||
|
@ -1132,35 +1103,119 @@ nla_put_failure:
|
|||
}
|
||||
|
||||
/**
|
||||
* Change link attributes
|
||||
* @arg sk Netlink socket.
|
||||
* @arg old link to be changed
|
||||
* @arg tmpl template with requested changes
|
||||
* Build a netlink message requesting the modification of a link
|
||||
* @arg orig original link to change
|
||||
* @arg changes link containing the changes to be made
|
||||
* @arg flags additional netlink message flags
|
||||
* @arg result pointer to store resulting netlink message
|
||||
*
|
||||
* The behaviour of this function is identical to rtnl_link_change() with
|
||||
* the exception that it will not send the message but return it in the
|
||||
* provided return pointer instead.
|
||||
*
|
||||
* @see rtnl_link_change()
|
||||
*
|
||||
* @note The resulting message will have message type set to RTM_NEWLINK
|
||||
* which may not work with older kernels. You may have to modify it
|
||||
* to RTM_SETLINK (does not allow changing link info attributes) to
|
||||
* have the change request work with older kernels.
|
||||
*
|
||||
* @return 0 on success or a negative error code.
|
||||
*/
|
||||
int rtnl_link_build_change_request(struct rtnl_link *orig,
|
||||
struct rtnl_link *changes, int flags,
|
||||
struct nl_msg **result)
|
||||
{
|
||||
struct ifinfomsg ifi = {
|
||||
.ifi_family = orig->l_family,
|
||||
.ifi_index = orig->l_index,
|
||||
};
|
||||
int err;
|
||||
|
||||
if (changes->ce_mask & LINK_ATTR_FLAGS) {
|
||||
ifi.ifi_flags = orig->l_flags & ~changes->l_flag_mask;
|
||||
ifi.ifi_flags |= changes->l_flags;
|
||||
}
|
||||
|
||||
if (changes->l_family && changes->l_family != orig->l_family) {
|
||||
APPBUG("link change: family is immutable");
|
||||
return -NLE_IMMUTABLE;
|
||||
}
|
||||
|
||||
/* Avoid unnecessary name change requests */
|
||||
if (orig->ce_mask & LINK_ATTR_IFINDEX &&
|
||||
orig->ce_mask & LINK_ATTR_IFNAME &&
|
||||
changes->ce_mask & LINK_ATTR_IFNAME &&
|
||||
!strcmp(orig->l_name, changes->l_name))
|
||||
changes->ce_mask &= ~LINK_ATTR_IFNAME;
|
||||
|
||||
if ((err = build_link_msg(RTM_NEWLINK, &ifi, changes, flags, result)) < 0)
|
||||
goto errout;
|
||||
|
||||
return 0;
|
||||
|
||||
errout:
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change link
|
||||
* @arg sk netlink socket.
|
||||
* @arg orig original link to be changed
|
||||
* @arg changes link containing the changes to be made
|
||||
* @arg flags additional netlink message flags
|
||||
*
|
||||
* Builds a new netlink message by calling rtnl_link_build_change_request(),
|
||||
* sends the request to the kernel and waits for the next ACK to be
|
||||
* received, i.e. blocks until the request has been processed.
|
||||
* Builds a \c RTM_NEWLINK netlink message requesting the change of
|
||||
* a network link. If -EOPNOTSUPP is returned by the kernel, the
|
||||
* message type will be changed to \c RTM_SETLINK and the message is
|
||||
* resent to work around older kernel versions.
|
||||
*
|
||||
* @return 0 on success or a negative error code
|
||||
* @note Not all attributes can be changed, see
|
||||
* \ref link_changeable "Changeable Attributes" for more details.
|
||||
* The link to be changed is looked up based on the interface index
|
||||
* supplied in the \p orig link. Optionaly the link name is used but
|
||||
* only if no interface index is provided, otherwise providing an
|
||||
* link name will result in the link name being changed.
|
||||
*
|
||||
* If no matching link exists, the function will return
|
||||
* -NLE_OBJ_NOTFOUND.
|
||||
*
|
||||
* After sending, the function will wait for the ACK or an eventual
|
||||
* error message to be received and will therefore block until the
|
||||
* operation has been completed.
|
||||
*
|
||||
* @note Disabling auto-ack (nl_socket_disable_auto_ack()) will cause
|
||||
* this function to return immediately after sending. In this case,
|
||||
* it is the responsibility of the caller to handle any error
|
||||
* messages returned.
|
||||
*
|
||||
* @note The link name can only be changed if the link has been put
|
||||
* in opertional down state. (~IF_UP)
|
||||
*
|
||||
* @return 0 on success or a negative error code.
|
||||
*/
|
||||
int rtnl_link_change(struct nl_sock *sk, struct rtnl_link *old,
|
||||
struct rtnl_link *tmpl, int flags)
|
||||
int rtnl_link_change(struct nl_sock *sk, struct rtnl_link *orig,
|
||||
struct rtnl_link *changes, int flags)
|
||||
{
|
||||
struct nl_msg *msg;
|
||||
int err;
|
||||
|
||||
if ((err = rtnl_link_build_change_request(old, tmpl, flags, &msg)) < 0)
|
||||
return err;
|
||||
|
||||
err = nl_send_auto_complete(sk, msg);
|
||||
nlmsg_free(msg);
|
||||
err = rtnl_link_build_change_request(orig, changes, flags, &msg);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return wait_for_ack(sk);
|
||||
retry:
|
||||
err = nl_send_auto_complete(sk, msg);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
|
||||
err = wait_for_ack(sk);
|
||||
if (err == -NLE_OPNOTSUPP && msg->nm_nlh->nlmsg_type == RTM_NEWLINK) {
|
||||
msg->nm_nlh->nlmsg_type = RTM_SETLINK;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
errout:
|
||||
nlmsg_free(msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Reference in a new issue