| /* |
| * 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 <net/if.h> |
| #include <netinet/in.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); |
| } |