2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Contact: Mateusz Malicki <m.malicki2@samsung.com>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License
21 * @author Mateusz Malicki (m.malicki2@samsung.com)
22 * @brief Network devices management functions definition
27 #include "netlink/netlink-message.hpp"
28 #include "utils/make-clean.hpp"
29 #include "utils/exception.hpp"
31 #include "exception.hpp"
32 #include "logger/logger.hpp"
42 #include <boost/algorithm/string/split.hpp>
43 #include <boost/algorithm/string/classification.hpp>
47 #include <sys/ioctl.h>
48 #include <arpa/inet.h>
50 #include <linux/rtnetlink.h>
51 #include <linux/veth.h>
52 #include <linux/sockios.h>
53 #include <linux/if_link.h>
54 #include <linux/rtnetlink.h>
55 #include <linux/if_bridge.h>
57 //IFLA_BRIDGE_FLAGS and BRIDGE_FLAGS_MASTER
58 //should be defined in linux/if_bridge.h since kernel v3.7
59 #ifndef IFLA_BRIDGE_FLAGS
60 #define IFLA_BRIDGE_FLAGS 0
62 #ifndef BRIDGE_FLAGS_MASTER
63 #define BRIDGE_FLAGS_MASTER 1
67 using namespace utils;
68 using namespace vasum::netlink;
75 string getUniqueVethName()
77 auto find = [](const ifaddrs* ifaddr, const string& name) -> bool {
78 for (const ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
79 if (name == ifa->ifa_name) {
91 newName = "veth0" + to_string(++i);
92 } while (find(ifaddr, newName));
98 uint32_t getInterfaceIndex(const string& name) {
99 uint32_t index = if_nametoindex(name.c_str());
101 LOGE("Can't get " << name << " interface index (" << getSystemErrorMessage() << ")");
102 throw ZoneOperationException("Can't find interface");
107 uint32_t getInterfaceIndex(const string& name, pid_t nsPid) {
108 NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST | NLM_F_ACK);
109 ifinfomsg infoPeer = utils::make_clean<ifinfomsg>();
110 infoPeer.ifi_family = AF_UNSPEC;
111 infoPeer.ifi_change = 0xFFFFFFFF;
113 .put(IFLA_IFNAME, name);
114 NetlinkResponse response = send(nlm, nsPid);
115 if (!response.hasMessage()) {
116 throw VasumException("Can't get interface index");
119 response.fetch(infoPeer);
120 return infoPeer.ifi_index;
123 int getIpFamily(const std::string& ip)
125 return ip.find(':') == std::string::npos ? AF_INET : AF_INET6;
128 void validateNetdevName(const string& name)
130 if (name.size() <= 1 || name.size() >= IFNAMSIZ) {
131 throw ZoneOperationException("Invalid netdev name format");
135 void createPipedNetdev(const string& netdev1, const string& netdev2)
137 validateNetdevName(netdev1);
138 validateNetdevName(netdev2);
140 NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK);
141 ifinfomsg infoPeer = utils::make_clean<ifinfomsg>();
142 infoPeer.ifi_family = AF_UNSPEC;
143 infoPeer.ifi_change = 0xFFFFFFFF;
145 .beginNested(IFLA_LINKINFO)
146 .put(IFLA_INFO_KIND, "veth")
147 .beginNested(IFLA_INFO_DATA)
148 .beginNested(VETH_INFO_PEER)
150 .put(IFLA_IFNAME, netdev2)
154 .put(IFLA_IFNAME, netdev1);
158 void attachToBridge(const string& bridge, const string& netdev)
160 validateNetdevName(bridge);
161 validateNetdevName(netdev);
163 uint32_t index = getInterfaceIndex(netdev);
164 int fd = socket(AF_LOCAL, SOCK_STREAM, 0);
166 LOGE("Can't open socket (" << getSystemErrorMessage() << ")");
167 throw ZoneOperationException("Can't attach to bridge");
170 struct ifreq ifr = utils::make_clean<ifreq>();
171 strncpy(ifr.ifr_name, bridge.c_str(), IFNAMSIZ);
172 ifr.ifr_ifindex = index;
173 int err = ioctl(fd, SIOCBRADDIF, &ifr);
176 //TODO: Close can be interrupted. Move util functions from ipc
178 LOGE("Can't attach to bridge (" + getSystemErrorMessage(error) + ")");
179 throw ZoneOperationException("Can't attach to bridge");
184 int setFlags(const string& name, uint32_t mask, uint32_t flags)
186 uint32_t index = getInterfaceIndex(name);
187 NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK);
188 ifinfomsg infoPeer = utils::make_clean<ifinfomsg>();
189 infoPeer.ifi_family = AF_UNSPEC;
190 infoPeer.ifi_index = index;
191 infoPeer.ifi_flags = flags;
192 // since kernel v2.6.22 ifi_change is used to change only selected flags;
193 infoPeer.ifi_change = mask;
199 void up(const string& netdev)
201 setFlags(netdev, IFF_UP, IFF_UP);
204 void moveToNS(const string& netdev, pid_t pid)
206 uint32_t index = getInterfaceIndex(netdev);
207 NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK);
208 ifinfomsg infopeer = utils::make_clean<ifinfomsg>();
209 infopeer.ifi_family = AF_UNSPEC;
210 infopeer.ifi_index = index;
212 .put(IFLA_NET_NS_PID, pid);
216 void createMacvlan(const string& master, const string& slave, const macvlan_mode& mode)
218 validateNetdevName(master);
219 validateNetdevName(slave);
221 uint32_t index = getInterfaceIndex(master);
222 NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK);
223 ifinfomsg infopeer = utils::make_clean<ifinfomsg>();
224 infopeer.ifi_family = AF_UNSPEC;
225 infopeer.ifi_change = 0xFFFFFFFF;
227 .beginNested(IFLA_LINKINFO)
228 .put(IFLA_INFO_KIND, "macvlan")
229 .beginNested(IFLA_INFO_DATA)
230 .put(IFLA_MACVLAN_MODE, static_cast<uint32_t>(mode))
233 .put(IFLA_LINK, index)
234 .put(IFLA_IFNAME, slave);
238 std::vector<Attrs> getIpAddresses(const pid_t nsPid, int family, uint32_t index)
240 NetlinkMessage nlm(RTM_GETADDR, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
241 ifaddrmsg infoAddr = utils::make_clean<ifaddrmsg>();
242 infoAddr.ifa_family = family;
244 NetlinkResponse response = send(nlm, nsPid);
245 if (!response.hasMessage()) {
246 //There is no interfaces with addresses
247 return std::vector<Attrs>();
250 std::vector<Attrs> addresses;
251 while (response.hasMessage()) {
253 response.fetch(addrmsg);
254 if (addrmsg.ifa_index == index) {
256 attrs.push_back(make_tuple("prefixlen", std::to_string(addrmsg.ifa_prefixlen)));
257 attrs.push_back(make_tuple("flags", std::to_string(addrmsg.ifa_flags)));
258 attrs.push_back(make_tuple("scope", std::to_string(addrmsg.ifa_scope)));
259 attrs.push_back(make_tuple("family", std::to_string(addrmsg.ifa_family)));
260 while (response.hasAttribute()) {
261 assert(INET6_ADDRSTRLEN >= INET_ADDRSTRLEN);
262 char buf[INET6_ADDRSTRLEN];
265 const void* addr = NULL;
266 int attrType = response.getAttributeType();
269 if (family == AF_INET6) {
270 response.fetch(IFA_ADDRESS, addr6);
273 assert(family == AF_INET);
274 response.fetch(IFA_ADDRESS, addr4);
277 addr = inet_ntop(family, addr, buf, sizeof(buf));
279 LOGE("Can't convert ip address: " << getSystemErrorMessage());
280 throw VasumException("Can't get ip address");
282 attrs.push_back(make_tuple("ip", buf));
285 response.skipAttribute();
289 addresses.push_back(std::move(attrs));
291 response.fetchNextMessage();
296 void setIpAddresses(const pid_t nsPid,
297 const uint32_t index,
301 NetlinkMessage nlm(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK);
302 ifaddrmsg infoAddr = utils::make_clean<ifaddrmsg>();
303 infoAddr.ifa_family = family;
304 infoAddr.ifa_index = index;
305 for (const auto& attr : attrs) {
306 if (get<0>(attr) == "prefixlen") {
307 infoAddr.ifa_prefixlen = stoul(get<1>(attr));
309 if (get<0>(attr) == "flags") {
310 infoAddr.ifa_flags = stoul(get<1>(attr));
312 if (get<0>(attr) == "scope") {
313 infoAddr.ifa_scope = stoul(get<1>(attr));
317 for (const auto& attr : attrs) {
318 if (get<0>(attr) == "ip") {
319 if (family == AF_INET6) {
321 if (inet_pton(AF_INET6, get<1>(attr).c_str(), &addr6) != 1) {
322 throw VasumException("Can't set ipv4 address");
324 nlm.put(IFA_ADDRESS, addr6);
325 nlm.put(IFA_LOCAL, addr6);
327 assert(family == AF_INET);
329 if (inet_pton(AF_INET, get<1>(attr).c_str(), &addr4) != 1) {
330 throw VasumException("Can't set ipv6 address");
332 nlm.put(IFA_LOCAL, addr4);
339 void deleteIpAddress(const pid_t nsPid,
340 const uint32_t index,
341 const std::string& ip,
345 NetlinkMessage nlm(RTM_DELADDR, NLM_F_REQUEST | NLM_F_ACK);
346 ifaddrmsg infoAddr = utils::make_clean<ifaddrmsg>();
347 infoAddr.ifa_family = family;
348 infoAddr.ifa_index = index;
349 infoAddr.ifa_prefixlen = prefixlen;
351 if (family == AF_INET6) {
353 if (inet_pton(AF_INET6, ip.c_str(), &addr6) != 1) {
354 throw VasumException("Can't delete ipv6 address");
356 nlm.put(IFA_ADDRESS, addr6);
357 nlm.put(IFA_LOCAL, addr6);
359 assert(family == AF_INET);
361 if (inet_pton(AF_INET, ip.c_str(), &addr4) != 1) {
362 throw VasumException("Can't delete ipv4 address");
364 nlm.put(IFA_LOCAL, addr4);
371 void createVeth(const pid_t& nsPid, const string& nsDev, const string& hostDev)
373 string hostVeth = getUniqueVethName();
374 LOGT("Creating veth: bridge: " << hostDev << ", port: " << hostVeth << ", zone: " << nsDev);
375 createPipedNetdev(nsDev, hostVeth);
377 attachToBridge(hostDev, hostVeth);
379 moveToNS(nsDev, nsPid);
380 } catch(const exception& ex) {
382 destroyNetdev(hostVeth);
383 } catch (const exception& ex) {
384 LOGE("Can't destroy netdev pipe: " << hostVeth << ", " << nsDev);
390 void createMacvlan(const pid_t& nsPid,
392 const string& hostDev,
393 const macvlan_mode& mode)
395 LOGT("Creating macvlan: host: " << hostDev << ", zone: " << nsDev << ", mode: " << mode);
396 createMacvlan(hostDev, nsDev, mode);
399 moveToNS(nsDev, nsPid);
400 } catch(const exception& ex) {
402 destroyNetdev(nsDev);
403 } catch (const exception& ex) {
404 LOGE("Can't destroy netdev: " << nsDev);
410 void movePhys(const pid_t& nsPid, const string& devId)
412 LOGT("Creating phys: dev: " << devId);
413 moveToNS(devId, nsPid);
416 std::vector<std::string> listNetdev(const pid_t& nsPid)
418 NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST|NLM_F_DUMP|NLM_F_ROOT);
419 ifinfomsg info = utils::make_clean<ifinfomsg>();
420 info.ifi_family = AF_PACKET;
422 NetlinkResponse response = send(nlm, nsPid);
423 std::vector<std::string> interfaces;
424 while (response.hasMessage()) {
426 response.skip<ifinfomsg>();
427 response.fetch(IFLA_IFNAME, ifName);
428 interfaces.push_back(ifName);
429 response.fetchNextMessage();
434 void destroyNetdev(const string& netdev, const pid_t pid)
436 LOGT("Destroying netdev: " << netdev);
437 validateNetdevName(netdev);
439 NetlinkMessage nlm(RTM_DELLINK, NLM_F_REQUEST|NLM_F_ACK);
440 ifinfomsg infopeer = utils::make_clean<ifinfomsg>();
441 infopeer.ifi_family = AF_UNSPEC;
442 infopeer.ifi_change = 0xFFFFFFFF;
444 .put(IFLA_IFNAME, netdev);
448 void createBridge(const string& netdev)
450 LOGT("Creating bridge: " << netdev);
451 validateNetdevName(netdev);
453 NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK);
454 ifinfomsg infoPeer = utils::make_clean<ifinfomsg>();
455 infoPeer.ifi_family = AF_UNSPEC;
456 infoPeer.ifi_change = 0xFFFFFFFF;
458 .beginNested(IFLA_LINKINFO)
459 .put(IFLA_INFO_KIND, "bridge")
460 .beginNested(IFLA_INFO_DATA)
461 .beginNested(IFLA_AF_SPEC)
462 .put<uint32_t>(IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_MASTER)
466 .put(IFLA_IFNAME, netdev);
470 Attrs getAttrs(const pid_t nsPid, const std::string& netdev)
472 auto joinAddresses = [](const Attrs& attrs) -> std::string {
475 for (const auto& attr : attrs) {
476 ss << (first ? "" : ",") << get<0>(attr) << ":" << get<1>(attr);
482 LOGT("Getting network device informations: " << netdev);
483 validateNetdevName(netdev);
485 NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST | NLM_F_ACK);
486 ifinfomsg infoPeer = utils::make_clean<ifinfomsg>();
487 infoPeer.ifi_family = AF_UNSPEC;
488 infoPeer.ifi_change = 0xFFFFFFFF;
490 .put(IFLA_IFNAME, netdev);
491 NetlinkResponse response = send(nlm, nsPid);
492 if (!response.hasMessage()) {
493 throw VasumException("Can't get interface information");
495 response.fetch(infoPeer);
498 while (response.hasAttribute()) {
500 int attrType = response.getAttributeType();
503 response.fetch(IFLA_MTU, mtu);
504 attrs.push_back(make_tuple("mtu", std::to_string(mtu)));
507 response.fetch(IFLA_LINK, link);
508 attrs.push_back(make_tuple("link", std::to_string(link)));
511 response.skipAttribute();
515 attrs.push_back(make_tuple("flags", std::to_string(infoPeer.ifi_flags)));
516 attrs.push_back(make_tuple("type", std::to_string(infoPeer.ifi_type)));
517 for (const auto& address : getIpAddresses(nsPid, AF_INET, infoPeer.ifi_index)) {
518 attrs.push_back(make_tuple("ipv4", joinAddresses(address)));
520 for (const auto& address : getIpAddresses(nsPid, AF_INET6, infoPeer.ifi_index)) {
521 attrs.push_back(make_tuple("ipv6", joinAddresses(address)));
527 void setAttrs(const pid_t nsPid, const std::string& netdev, const Attrs& attrs)
529 const set<string> supportedAttrs{"flags", "change", "type", "mtu", "link", "ipv4", "ipv6"};
531 LOGT("Setting network device informations: " << netdev);
532 validateNetdevName(netdev);
533 for (const auto& attr : attrs) {
534 if (supportedAttrs.find(get<0>(attr)) == supportedAttrs.end()) {
535 throw VasumException("Unsupported attribute: " + get<0>(attr));
539 NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK);
540 ifinfomsg infoPeer = utils::make_clean<ifinfomsg>();
541 infoPeer.ifi_family = AF_UNSPEC;
542 infoPeer.ifi_index = getInterfaceIndex(netdev, nsPid);
543 infoPeer.ifi_change = 0xFFFFFFFF;
544 for (const auto& attr : attrs) {
545 if (get<0>(attr) == "flags") {
546 infoPeer.ifi_flags = stoul(get<1>(attr));
548 if (get<0>(attr) == "change") {
549 infoPeer.ifi_change = stoul(get<1>(attr));
551 if (get<0>(attr) == "type") {
552 infoPeer.ifi_type = stoul(get<1>(attr));
556 for (const auto& attr : attrs) {
557 if (get<0>(attr) == "mtu") {
558 nlm.put<uint32_t>(IFLA_MTU, stoul(get<1>(attr)));
560 if (get<0>(attr) == "link") {
561 nlm.put<uint32_t>(IFLA_LINK, stoul(get<1>(attr)));
565 NetlinkResponse response = send(nlm, nsPid);
566 if (!response.hasMessage()) {
567 throw VasumException("Can't set interface information");
570 //TODO: Multiple addresses should be set at once (add support NLM_F_MULTI to NetlinkMessage).
573 for (const auto& attr : attrs) {
574 if (get<0>(attr) == "ipv4") {
575 ipv4.push_back(get<1>(attr));
577 if (get<0>(attr) == "ipv6") {
578 ipv6.push_back(get<1>(attr));
582 auto setIp = [nsPid](const vector<string>& ips, uint32_t index, int family) -> void {
583 using namespace boost::algorithm;
584 for (const auto& ip : ips) {
586 vector<string> params;
587 for (const auto& addrAttr : split(params, ip, is_any_of(","))) {
588 size_t pos = addrAttr.find(":");
589 if (pos == string::npos || pos == addrAttr.length()) {
590 LOGE("Wrong input data format: ill formed address attribute: " << addrAttr);
591 VasumException("Wrong input data format: ill formed address attribute");
593 attrs.push_back(make_tuple(addrAttr.substr(0, pos), addrAttr.substr(pos + 1)));
595 setIpAddresses(nsPid, index, attrs, family);
599 setIp(ipv4, infoPeer.ifi_index, AF_INET);
600 setIp(ipv6, infoPeer.ifi_index, AF_INET6);
603 void deleteIpAddress(const pid_t nsPid,
604 const std::string& netdev,
605 const std::string& ip)
607 uint32_t index = getInterfaceIndex(netdev, nsPid);
608 size_t slash = ip.find('/');
609 if (slash == string::npos) {
610 LOGE("Wrong address format: it is not CIDR notation: can't find '/'");
611 throw VasumException("Wrong address format");
615 prefixlen = stoi(ip.substr(slash + 1));
616 } catch (const std::exception& ex) {
617 LOGE("Wrong address format: invalid prefixlen");
618 throw VasumException("Wrong address format: invalid prefixlen");
620 deleteIpAddress(nsPid, index, ip.substr(0, slash), prefixlen, getIpFamily(ip));