You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
4.8 KiB
180 lines
4.8 KiB
/* |
|
* Copyright 2012 Daniel Drown <dan-android@drown.org> |
|
* |
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
|
* you may not use this file except in compliance with the License. |
|
* You may obtain a copy of the License at |
|
* |
|
* http://www.apache.org/licenses/LICENSE-2.0 |
|
* |
|
* Unless required by applicable law or agreed to in writing, software |
|
* distributed under the License is distributed on an "AS IS" BASIS, |
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
* See the License for the specific language governing permissions and |
|
* limitations under the License. |
|
* |
|
* setif.c - network interface configuration |
|
*/ |
|
#include <errno.h> |
|
#include <netinet/in.h> |
|
#include <net/if.h> |
|
|
|
#include <linux/rtnetlink.h> |
|
#include <netlink/handlers.h> |
|
#include <netlink/msg.h> |
|
|
|
#include "logging.h" |
|
#include "netlink_msg.h" |
|
|
|
#define DEBUG_OPTNAME(a) case (a): { optname = #a; break; } |
|
|
|
/* function: add_address |
|
* adds an IP address to/from an interface, returns 0 on success and <0 on failure |
|
* ifname - name of interface to change |
|
* family - address family (AF_INET, AF_INET6) |
|
* address - pointer to a struct in_addr or in6_addr |
|
* prefixlen - bitlength of network (example: 24 for AF_INET's 255.255.255.0) |
|
* broadcast - broadcast address (only for AF_INET, ignored for AF_INET6) |
|
*/ |
|
int add_address(const char *ifname, int family, const void *address, int prefixlen, const void *broadcast) { |
|
int retval; |
|
size_t addr_size; |
|
struct ifaddrmsg ifa; |
|
struct nl_msg *msg = NULL; |
|
|
|
addr_size = inet_family_size(family); |
|
if(addr_size == 0) { |
|
retval = -EAFNOSUPPORT; |
|
goto cleanup; |
|
} |
|
|
|
memset(&ifa, 0, sizeof(ifa)); |
|
if (!(ifa.ifa_index = if_nametoindex(ifname))) { |
|
retval = -ENODEV; |
|
goto cleanup; |
|
} |
|
ifa.ifa_family = family; |
|
ifa.ifa_prefixlen = prefixlen; |
|
ifa.ifa_scope = RT_SCOPE_UNIVERSE; |
|
|
|
msg = nlmsg_alloc_ifaddr(RTM_NEWADDR, NLM_F_ACK | NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, &ifa); |
|
if(!msg) { |
|
retval = -ENOMEM; |
|
goto cleanup; |
|
} |
|
|
|
if(nla_put(msg, IFA_LOCAL, addr_size, address) < 0) { |
|
retval = -ENOMEM; |
|
goto cleanup; |
|
} |
|
if(family == AF_INET6) { |
|
// AF_INET6 gets IFA_LOCAL + IFA_ADDRESS |
|
if(nla_put(msg, IFA_ADDRESS, addr_size, address) < 0) { |
|
retval = -ENOMEM; |
|
goto cleanup; |
|
} |
|
} else if(family == AF_INET) { |
|
// AF_INET gets IFA_LOCAL + IFA_BROADCAST |
|
if(nla_put(msg, IFA_BROADCAST, addr_size, broadcast) < 0) { |
|
retval = -ENOMEM; |
|
goto cleanup; |
|
} |
|
} else { |
|
retval = -EAFNOSUPPORT; |
|
goto cleanup; |
|
} |
|
|
|
retval = netlink_sendrecv(msg); |
|
|
|
cleanup: |
|
if(msg) |
|
nlmsg_free(msg); |
|
|
|
return retval; |
|
} |
|
|
|
/* function: if_up |
|
* sets interface link state to up and sets mtu, returns 0 on success and <0 on failure |
|
* ifname - interface name to change |
|
* mtu - new mtu |
|
*/ |
|
int if_up(const char *ifname, int mtu) { |
|
int retval = -1; |
|
struct ifinfomsg ifi; |
|
struct nl_msg *msg = NULL; |
|
|
|
memset(&ifi, 0, sizeof(ifi)); |
|
if (!(ifi.ifi_index = if_nametoindex(ifname))) { |
|
retval = -ENODEV; |
|
goto cleanup; |
|
} |
|
ifi.ifi_change = IFF_UP; |
|
ifi.ifi_flags = IFF_UP; |
|
|
|
msg = nlmsg_alloc_ifinfo(RTM_SETLINK, NLM_F_ACK | NLM_F_REQUEST | NLM_F_ROOT, &ifi); |
|
if(!msg) { |
|
retval = -ENOMEM; |
|
goto cleanup; |
|
} |
|
|
|
if(nla_put(msg, IFLA_MTU, 4, &mtu) < 0) { |
|
retval = -ENOMEM; |
|
goto cleanup; |
|
} |
|
|
|
retval = netlink_sendrecv(msg); |
|
|
|
cleanup: |
|
if(msg) |
|
nlmsg_free(msg); |
|
|
|
return retval; |
|
} |
|
|
|
static int do_anycast_setsockopt(int sock, int what, struct in6_addr *addr, int ifindex) { |
|
struct ipv6_mreq mreq = { *addr, ifindex }; |
|
char *optname; |
|
int ret; |
|
|
|
switch (what) { |
|
DEBUG_OPTNAME(IPV6_JOIN_ANYCAST) |
|
DEBUG_OPTNAME(IPV6_LEAVE_ANYCAST) |
|
default: |
|
optname = "???"; |
|
break; |
|
} |
|
|
|
ret = setsockopt(sock, SOL_IPV6, what, &mreq, sizeof(mreq)); |
|
if (ret) { |
|
logmsg(ANDROID_LOG_ERROR, "%s: setsockopt(%s): %s", __func__, optname, strerror(errno)); |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
/* function: add_anycast_address |
|
* adds an anycast IPv6 address to an interface, returns 0 on success and <0 on failure |
|
* sock - the socket to add the address to |
|
* addr - the IP address to add |
|
* ifname - name of interface to add the address to |
|
*/ |
|
int add_anycast_address(int sock, struct in6_addr *addr, const char *ifname) { |
|
int ifindex; |
|
|
|
ifindex = if_nametoindex(ifname); |
|
if (!ifindex) { |
|
logmsg(ANDROID_LOG_ERROR, "%s: unknown ifindex for interface %s", __func__, ifname); |
|
return -ENODEV; |
|
} |
|
|
|
return do_anycast_setsockopt(sock, IPV6_JOIN_ANYCAST, addr, ifindex); |
|
} |
|
|
|
/* function: del_anycast_address |
|
* removes an anycast IPv6 address from the system, returns 0 on success and <0 on failure |
|
* sock - the socket to remove from, must have had the address added via add_anycast_address |
|
* addr - the IP address to remove |
|
*/ |
|
int del_anycast_address(int sock, struct in6_addr *addr) { |
|
return do_anycast_setsockopt(sock, IPV6_LEAVE_ANYCAST, addr, 0); |
|
}
|
|
|