1/* 2 * Copyright 2012 Daniel Drown <dan-android@drown.org> 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 * setif.c - network interface configuration 17 */ 18#include <errno.h> 19#include <netinet/in.h> 20#include <net/if.h> 21 22#include <linux/rtnetlink.h> 23#include <netlink/handlers.h> 24#include <netlink/msg.h> 25 26#include "logging.h" 27#include "netlink_msg.h" 28 29#define DEBUG_OPTNAME(a) case (a): { optname = #a; break; } 30 31/* function: add_address 32 * adds an IP address to/from an interface, returns 0 on success and <0 on failure 33 * ifname - name of interface to change 34 * family - address family (AF_INET, AF_INET6) 35 * address - pointer to a struct in_addr or in6_addr 36 * prefixlen - bitlength of network (example: 24 for AF_INET's 255.255.255.0) 37 * broadcast - broadcast address (only for AF_INET, ignored for AF_INET6) 38 */ 39int add_address(const char *ifname, int family, const void *address, int prefixlen, const void *broadcast) { 40 int retval; 41 size_t addr_size; 42 struct ifaddrmsg ifa; 43 struct nl_msg *msg = NULL; 44 45 addr_size = inet_family_size(family); 46 if(addr_size == 0) { 47 retval = -EAFNOSUPPORT; 48 goto cleanup; 49 } 50 51 memset(&ifa, 0, sizeof(ifa)); 52 if (!(ifa.ifa_index = if_nametoindex(ifname))) { 53 retval = -ENODEV; 54 goto cleanup; 55 } 56 ifa.ifa_family = family; 57 ifa.ifa_prefixlen = prefixlen; 58 ifa.ifa_scope = RT_SCOPE_UNIVERSE; 59 60 msg = nlmsg_alloc_ifaddr(RTM_NEWADDR, NLM_F_ACK | NLM_F_REQUEST | NLM_F_CREATE | NLM_F_REPLACE, &ifa); 61 if(!msg) { 62 retval = -ENOMEM; 63 goto cleanup; 64 } 65 66 if(nla_put(msg, IFA_LOCAL, addr_size, address) < 0) { 67 retval = -ENOMEM; 68 goto cleanup; 69 } 70 if(family == AF_INET6) { 71 // AF_INET6 gets IFA_LOCAL + IFA_ADDRESS 72 if(nla_put(msg, IFA_ADDRESS, addr_size, address) < 0) { 73 retval = -ENOMEM; 74 goto cleanup; 75 } 76 } else if(family == AF_INET) { 77 // AF_INET gets IFA_LOCAL + IFA_BROADCAST 78 if(nla_put(msg, IFA_BROADCAST, addr_size, broadcast) < 0) { 79 retval = -ENOMEM; 80 goto cleanup; 81 } 82 } else { 83 retval = -EAFNOSUPPORT; 84 goto cleanup; 85 } 86 87 retval = netlink_sendrecv(msg); 88 89cleanup: 90 if(msg) 91 nlmsg_free(msg); 92 93 return retval; 94} 95 96/* function: if_up 97 * sets interface link state to up and sets mtu, returns 0 on success and <0 on failure 98 * ifname - interface name to change 99 * mtu - new mtu 100 */ 101int if_up(const char *ifname, int mtu) { 102 int retval = -1; 103 struct ifinfomsg ifi; 104 struct nl_msg *msg = NULL; 105 106 memset(&ifi, 0, sizeof(ifi)); 107 if (!(ifi.ifi_index = if_nametoindex(ifname))) { 108 retval = -ENODEV; 109 goto cleanup; 110 } 111 ifi.ifi_change = IFF_UP; 112 ifi.ifi_flags = IFF_UP; 113 114 msg = nlmsg_alloc_ifinfo(RTM_SETLINK, NLM_F_ACK | NLM_F_REQUEST | NLM_F_ROOT, &ifi); 115 if(!msg) { 116 retval = -ENOMEM; 117 goto cleanup; 118 } 119 120 if(nla_put(msg, IFLA_MTU, 4, &mtu) < 0) { 121 retval = -ENOMEM; 122 goto cleanup; 123 } 124 125 retval = netlink_sendrecv(msg); 126 127cleanup: 128 if(msg) 129 nlmsg_free(msg); 130 131 return retval; 132} 133 134static int do_anycast_setsockopt(int sock, int what, struct in6_addr *addr, int ifindex) { 135 struct ipv6_mreq mreq = { *addr, ifindex }; 136 char *optname; 137 int ret; 138 139 switch (what) { 140 DEBUG_OPTNAME(IPV6_JOIN_ANYCAST) 141 DEBUG_OPTNAME(IPV6_LEAVE_ANYCAST) 142 default: 143 optname = "???"; 144 break; 145 } 146 147 ret = setsockopt(sock, SOL_IPV6, what, &mreq, sizeof(mreq)); 148 if (ret) { 149 logmsg(ANDROID_LOG_ERROR, "%s: setsockopt(%s): %s", __func__, optname, strerror(errno)); 150 } 151 152 return ret; 153} 154 155/* function: add_anycast_address 156 * adds an anycast IPv6 address to an interface, returns 0 on success and <0 on failure 157 * sock - the socket to add the address to 158 * addr - the IP address to add 159 * ifname - name of interface to add the address to 160 */ 161int add_anycast_address(int sock, struct in6_addr *addr, const char *ifname) { 162 int ifindex, s, ret; 163 164 ifindex = if_nametoindex(ifname); 165 if (!ifindex) { 166 logmsg(ANDROID_LOG_ERROR, "%s: unknown ifindex for interface %s", __func__, ifname); 167 return -ENODEV; 168 } 169 170 return do_anycast_setsockopt(sock, IPV6_JOIN_ANYCAST, addr, ifindex); 171} 172 173/* function: del_anycast_address 174 * removes an anycast IPv6 address from the system, returns 0 on success and <0 on failure 175 * sock - the socket to remove from, must have had the address added via add_anycast_address 176 * addr - the IP address to remove 177 */ 178int del_anycast_address(int sock, struct in6_addr *addr) { 179 return do_anycast_setsockopt(sock, IPV6_LEAVE_ANYCAST, addr, 0); 180} 181