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
66 using namespace utils;
67 using namespace vasum::netlink;
75 std::string getUniqueVethName()
77 auto find = [](const ifaddrs* ifaddr, const std::string& name) -> bool {
78 for (const ifaddrs* ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
79 if (name == ifa->ifa_name) {
91 newName = "veth0" + std::to_string(++i);
92 } while (find(ifaddr, newName));
98 uint32_t getInterfaceIndex(const std::string& name) {
99 uint32_t index = if_nametoindex(name.c_str());
101 const std::string msg = "Can't get " + name + " interface index (" + getSystemErrorMessage() + ")";
103 throw ZoneOperationException(msg);
108 uint32_t getInterfaceIndex(const std::string& name, pid_t nsPid) {
109 NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST | NLM_F_ACK);
110 ifinfomsg infoPeer = utils::make_clean<ifinfomsg>();
111 infoPeer.ifi_family = AF_UNSPEC;
112 infoPeer.ifi_change = 0xFFFFFFFF;
114 .put(IFLA_IFNAME, name);
115 NetlinkResponse response = send(nlm, nsPid);
116 if (!response.hasMessage()) {
117 throw VasumException("Can't get interface index");
120 response.fetch(infoPeer);
121 return infoPeer.ifi_index;
124 int getIpFamily(const std::string& ip)
126 return ip.find(':') == std::string::npos ? AF_INET : AF_INET6;
129 void validateNetdevName(const std::string& name)
131 if (name.size() <= 1 || name.size() >= IFNAMSIZ) {
132 throw ZoneOperationException("Invalid netdev name format");
136 void createPipedNetdev(const std::string& netdev1, const std::string& netdev2)
138 validateNetdevName(netdev1);
139 validateNetdevName(netdev2);
141 NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK);
142 ifinfomsg infoPeer = utils::make_clean<ifinfomsg>();
143 infoPeer.ifi_family = AF_UNSPEC;
144 infoPeer.ifi_change = 0xFFFFFFFF;
146 .beginNested(IFLA_LINKINFO)
147 .put(IFLA_INFO_KIND, "veth")
148 .beginNested(IFLA_INFO_DATA)
149 .beginNested(VETH_INFO_PEER)
151 .put(IFLA_IFNAME, netdev2)
155 .put(IFLA_IFNAME, netdev1);
159 void attachToBridge(const std::string& bridge, const std::string& netdev)
161 validateNetdevName(bridge);
162 validateNetdevName(netdev);
164 uint32_t index = getInterfaceIndex(netdev);
165 int fd = socket(AF_LOCAL, SOCK_STREAM, 0);
167 const std::string msg = "Can't open socket (" + getSystemErrorMessage() + ")";
169 throw ZoneOperationException(msg);
172 struct ifreq ifr = utils::make_clean<ifreq>();
173 strncpy(ifr.ifr_name, bridge.c_str(), IFNAMSIZ);
174 ifr.ifr_ifindex = index;
175 int err = ioctl(fd, SIOCBRADDIF, &ifr);
178 //TODO: Close can be interrupted. Move util functions from ipc
180 const std::string msg = "Can't attach to bridge (" + getSystemErrorMessage(error) + ")";
182 throw ZoneOperationException(msg);
187 int setFlags(const std::string& name, uint32_t mask, uint32_t flags)
189 uint32_t index = getInterfaceIndex(name);
190 NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK);
191 ifinfomsg infoPeer = utils::make_clean<ifinfomsg>();
192 infoPeer.ifi_family = AF_UNSPEC;
193 infoPeer.ifi_index = index;
194 infoPeer.ifi_flags = flags;
195 // since kernel v2.6.22 ifi_change is used to change only selected flags;
196 infoPeer.ifi_change = mask;
202 void up(const std::string& netdev)
204 setFlags(netdev, IFF_UP, IFF_UP);
207 void moveToNS(const std::string& netdev, pid_t pid)
209 uint32_t index = getInterfaceIndex(netdev);
210 NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK);
211 ifinfomsg infopeer = utils::make_clean<ifinfomsg>();
212 infopeer.ifi_family = AF_UNSPEC;
213 infopeer.ifi_index = index;
215 .put(IFLA_NET_NS_PID, pid);
219 void createMacvlan(const std::string& master, const std::string& slave, const macvlan_mode& mode)
221 validateNetdevName(master);
222 validateNetdevName(slave);
224 uint32_t index = getInterfaceIndex(master);
225 NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL|NLM_F_ACK);
226 ifinfomsg infopeer = utils::make_clean<ifinfomsg>();
227 infopeer.ifi_family = AF_UNSPEC;
228 infopeer.ifi_change = 0xFFFFFFFF;
230 .beginNested(IFLA_LINKINFO)
231 .put(IFLA_INFO_KIND, "macvlan")
232 .beginNested(IFLA_INFO_DATA)
233 .put(IFLA_MACVLAN_MODE, static_cast<uint32_t>(mode))
236 .put(IFLA_LINK, index)
237 .put(IFLA_IFNAME, slave);
241 std::vector<Attrs> getIpAddresses(const pid_t nsPid, int family, uint32_t index)
243 NetlinkMessage nlm(RTM_GETADDR, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
244 ifaddrmsg infoAddr = utils::make_clean<ifaddrmsg>();
245 infoAddr.ifa_family = family;
247 NetlinkResponse response = send(nlm, nsPid);
248 if (!response.hasMessage()) {
249 //There is no interfaces with addresses
250 return std::vector<Attrs>();
253 std::vector<Attrs> addresses;
254 while (response.hasMessage()) {
256 response.fetch(addrmsg);
257 if (addrmsg.ifa_index == index) {
259 attrs.push_back(make_tuple("prefixlen", std::to_string(addrmsg.ifa_prefixlen)));
260 attrs.push_back(make_tuple("flags", std::to_string(addrmsg.ifa_flags)));
261 attrs.push_back(make_tuple("scope", std::to_string(addrmsg.ifa_scope)));
262 attrs.push_back(make_tuple("family", std::to_string(addrmsg.ifa_family)));
263 while (response.hasAttribute()) {
264 assert(INET6_ADDRSTRLEN >= INET_ADDRSTRLEN);
265 char buf[INET6_ADDRSTRLEN];
268 const void* addr = NULL;
269 int attrType = response.getAttributeType();
272 if (family == AF_INET6) {
273 response.fetch(IFA_ADDRESS, addr6);
276 assert(family == AF_INET);
277 response.fetch(IFA_ADDRESS, addr4);
280 addr = inet_ntop(family, addr, buf, sizeof(buf));
282 const std::string msg = "Can't convert ip address: " + getSystemErrorMessage();
284 throw VasumException(msg);
286 attrs.push_back(std::make_tuple("ip", buf));
289 response.skipAttribute();
293 addresses.push_back(std::move(attrs));
295 response.fetchNextMessage();
300 void setIpAddresses(const pid_t nsPid,
301 const uint32_t index,
305 NetlinkMessage nlm(RTM_NEWADDR, NLM_F_CREATE | NLM_F_REQUEST | NLM_F_ACK);
306 ifaddrmsg infoAddr = utils::make_clean<ifaddrmsg>();
307 infoAddr.ifa_family = family;
308 infoAddr.ifa_index = index;
309 for (const auto& attr : attrs) {
310 if (get<0>(attr) == "prefixlen") {
311 infoAddr.ifa_prefixlen = stoul(get<1>(attr));
313 if (get<0>(attr) == "flags") {
314 infoAddr.ifa_flags = stoul(get<1>(attr));
316 if (get<0>(attr) == "scope") {
317 infoAddr.ifa_scope = stoul(get<1>(attr));
321 for (const auto& attr : attrs) {
322 if (get<0>(attr) == "ip") {
323 if (family == AF_INET6) {
325 if (inet_pton(AF_INET6, get<1>(attr).c_str(), &addr6) != 1) {
326 throw VasumException("Can't set ipv4 address");
328 nlm.put(IFA_ADDRESS, addr6);
329 nlm.put(IFA_LOCAL, addr6);
331 assert(family == AF_INET);
333 if (inet_pton(AF_INET, get<1>(attr).c_str(), &addr4) != 1) {
334 throw VasumException("Can't set ipv6 address");
336 nlm.put(IFA_LOCAL, addr4);
343 void deleteIpAddress(const pid_t nsPid,
344 const uint32_t index,
345 const std::string& ip,
349 NetlinkMessage nlm(RTM_DELADDR, NLM_F_REQUEST | NLM_F_ACK);
350 ifaddrmsg infoAddr = utils::make_clean<ifaddrmsg>();
351 infoAddr.ifa_family = family;
352 infoAddr.ifa_index = index;
353 infoAddr.ifa_prefixlen = prefixlen;
355 if (family == AF_INET6) {
357 if (inet_pton(AF_INET6, ip.c_str(), &addr6) != 1) {
358 throw VasumException("Can't delete ipv6 address");
360 nlm.put(IFA_ADDRESS, addr6);
361 nlm.put(IFA_LOCAL, addr6);
363 assert(family == AF_INET);
365 if (inet_pton(AF_INET, ip.c_str(), &addr4) != 1) {
366 throw VasumException("Can't delete ipv4 address");
368 nlm.put(IFA_LOCAL, addr4);
375 void createVeth(const pid_t& nsPid, const std::string& nsDev, const std::string& hostDev)
377 std::string hostVeth = getUniqueVethName();
378 LOGT("Creating veth: bridge: " << hostDev << ", port: " << hostVeth << ", zone: " << nsDev);
379 createPipedNetdev(nsDev, hostVeth);
381 attachToBridge(hostDev, hostVeth);
383 moveToNS(nsDev, nsPid);
384 } catch(const std::exception& ex) {
386 destroyNetdev(hostVeth);
387 } catch (const std::exception& ex) {
388 LOGE("Can't destroy netdev pipe: " << hostVeth << ", " << nsDev);
394 void createMacvlan(const pid_t& nsPid,
395 const std::string& nsDev,
396 const std::string& hostDev,
397 const macvlan_mode& mode)
399 LOGT("Creating macvlan: host: " << hostDev << ", zone: " << nsDev << ", mode: " << mode);
400 createMacvlan(hostDev, nsDev, mode);
403 moveToNS(nsDev, nsPid);
404 } catch(const std::exception& ex) {
406 destroyNetdev(nsDev);
407 } catch (const std::exception& ex) {
408 LOGE("Can't destroy netdev: " << nsDev);
414 void movePhys(const pid_t& nsPid, const std::string& devId)
416 LOGT("Creating phys: dev: " << devId);
417 moveToNS(devId, nsPid);
420 std::vector<std::string> listNetdev(const pid_t& nsPid)
422 NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST|NLM_F_DUMP|NLM_F_ROOT);
423 ifinfomsg info = utils::make_clean<ifinfomsg>();
424 info.ifi_family = AF_PACKET;
426 NetlinkResponse response = send(nlm, nsPid);
427 std::vector<std::string> interfaces;
428 while (response.hasMessage()) {
430 response.skip<ifinfomsg>();
431 // fetched value contains \0 terminator
432 int len = response.getAttributeLength();
433 response.fetch(IFLA_IFNAME, ifName, len - 1);
434 interfaces.push_back(ifName);
435 response.fetchNextMessage();
440 void destroyNetdev(const std::string& netdev, const pid_t pid)
442 LOGT("Destroying netdev: " << netdev);
443 validateNetdevName(netdev);
445 NetlinkMessage nlm(RTM_DELLINK, NLM_F_REQUEST|NLM_F_ACK);
446 ifinfomsg infopeer = utils::make_clean<ifinfomsg>();
447 infopeer.ifi_family = AF_UNSPEC;
448 infopeer.ifi_change = 0xFFFFFFFF;
450 .put(IFLA_IFNAME, netdev);
454 void createBridge(const std::string& netdev)
456 LOGT("Creating bridge: " << netdev);
457 validateNetdevName(netdev);
459 NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK);
460 ifinfomsg infoPeer = utils::make_clean<ifinfomsg>();
461 infoPeer.ifi_family = AF_UNSPEC;
462 infoPeer.ifi_change = 0xFFFFFFFF;
464 .beginNested(IFLA_LINKINFO)
465 .put(IFLA_INFO_KIND, "bridge")
466 .beginNested(IFLA_INFO_DATA)
467 .beginNested(IFLA_AF_SPEC)
468 .put<uint32_t>(IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_MASTER)
472 .put(IFLA_IFNAME, netdev);
476 Attrs getAttrs(const pid_t nsPid, const std::string& netdev)
478 auto joinAddresses = [](const Attrs& attrs) -> std::string {
480 std::stringstream ss;
481 for (const auto& attr : attrs) {
482 ss << (first ? "" : ",") << get<0>(attr) << ":" << get<1>(attr);
488 LOGT("Getting network device informations: " << netdev);
489 validateNetdevName(netdev);
491 NetlinkMessage nlm(RTM_GETLINK, NLM_F_REQUEST | NLM_F_ACK);
492 ifinfomsg infoPeer = utils::make_clean<ifinfomsg>();
493 infoPeer.ifi_family = AF_UNSPEC;
494 infoPeer.ifi_change = 0xFFFFFFFF;
496 .put(IFLA_IFNAME, netdev);
499 NetlinkResponse response = send(nlm, nsPid);
500 if (!response.hasMessage()) {
501 throw VasumException("Can't get interface information");
503 response.fetch(infoPeer);
505 while (response.hasAttribute()) {
507 int attrType = response.getAttributeType();
510 response.fetch(IFLA_MTU, mtu);
511 attrs.push_back(make_tuple("mtu", std::to_string(mtu)));
514 response.fetch(IFLA_LINK, link);
515 attrs.push_back(make_tuple("link", std::to_string(link)));
518 response.skipAttribute();
522 } catch (const std::exception& ex) {
524 throw VasumException(netdev + ": " + ex.what());
527 attrs.push_back(make_tuple("flags", std::to_string(infoPeer.ifi_flags)));
528 attrs.push_back(make_tuple("type", std::to_string(infoPeer.ifi_type)));
529 for (const auto& address : getIpAddresses(nsPid, AF_INET, infoPeer.ifi_index)) {
530 attrs.push_back(make_tuple("ipv4", joinAddresses(address)));
533 for (const auto& address : getIpAddresses(nsPid, AF_INET6, infoPeer.ifi_index)) {
534 attrs.push_back(make_tuple("ipv6", joinAddresses(address)));
540 void setAttrs(const pid_t nsPid, const std::string& netdev, const Attrs& attrs)
542 const std::set<std::string> supportedAttrs{"flags", "change", "type", "mtu", "link", "ipv4", "ipv6"};
544 LOGT("Setting network device informations: " << netdev);
545 validateNetdevName(netdev);
546 for (const auto& attr : attrs) {
547 if (supportedAttrs.find(get<0>(attr)) == supportedAttrs.end()) {
548 throw VasumException("Unsupported attribute: " + get<0>(attr));
552 NetlinkMessage nlm(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK);
553 ifinfomsg infoPeer = utils::make_clean<ifinfomsg>();
554 infoPeer.ifi_family = AF_UNSPEC;
555 infoPeer.ifi_index = getInterfaceIndex(netdev, nsPid);
556 infoPeer.ifi_change = 0xFFFFFFFF;
557 for (const auto& attr : attrs) {
558 if (get<0>(attr) == "flags") {
559 infoPeer.ifi_flags = stoul(get<1>(attr));
561 if (get<0>(attr) == "change") {
562 infoPeer.ifi_change = stoul(get<1>(attr));
564 if (get<0>(attr) == "type") {
565 infoPeer.ifi_type = stoul(get<1>(attr));
569 for (const auto& attr : attrs) {
570 if (get<0>(attr) == "mtu") {
571 nlm.put<uint32_t>(IFLA_MTU, stoul(get<1>(attr)));
573 if (get<0>(attr) == "link") {
574 nlm.put<uint32_t>(IFLA_LINK, stoul(get<1>(attr)));
578 NetlinkResponse response = send(nlm, nsPid);
579 if (!response.hasMessage()) {
580 throw VasumException("Can't set interface information");
583 //TODO: Multiple addresses should be set at once (add support NLM_F_MULTI to NetlinkMessage).
584 std::vector<std::string> ipv4;
585 std::vector<std::string> ipv6;
586 for (const auto& attr : attrs) {
587 if (get<0>(attr) == "ipv4") {
588 ipv4.push_back(get<1>(attr));
590 if (get<0>(attr) == "ipv6") {
591 ipv6.push_back(get<1>(attr));
595 auto setIp = [nsPid](const std::vector<std::string>& ips, uint32_t index, int family) -> void {
596 using namespace boost::algorithm;
597 for (const auto& ip : ips) {
599 std::vector<std::string> params;
600 for (const auto& addrAttr : split(params, ip, is_any_of(","))) {
601 size_t pos = addrAttr.find(":");
602 if (pos == std::string::npos || pos == addrAttr.length()) {
603 const std::string msg = "Wrong input data format: ill formed address attribute: " + addrAttr;
605 throw VasumException(msg);
607 attrs.push_back(make_tuple(addrAttr.substr(0, pos), addrAttr.substr(pos + 1)));
609 setIpAddresses(nsPid, index, attrs, family);
613 setIp(ipv4, infoPeer.ifi_index, AF_INET);
614 setIp(ipv6, infoPeer.ifi_index, AF_INET6);
617 void deleteIpAddress(const pid_t nsPid,
618 const std::string& netdev,
619 const std::string& ip)
621 uint32_t index = getInterfaceIndex(netdev, nsPid);
622 size_t slash = ip.find('/');
623 if (slash == std::string::npos) {
624 const std::string msg = "Wrong address format: it is not CIDR notation: can't find '/'";
626 throw VasumException(msg);
630 prefixlen = stoi(ip.substr(slash + 1));
631 } catch (const std::exception& ex) {
632 const std::string msg = "Wrong address format: invalid prefixlen";
634 throw VasumException(msg);
636 deleteIpAddress(nsPid, index, ip.substr(0, slash), prefixlen, getIpFamily(ip));