Thursday, January 13, 2011

Communicating with the Kernel via Netlink, Part II

OK--continuing on from where we left off with part I. The previous post ran through key details of the overall layout of the objects, aspects of the Netlink interface and some source snippets from the listener and sender classes. This time I want to cover the event object and parsing of the netlink data object. Plus, source code is located at the end of this article.



Event Manager

The event manager acts somewhat like a buffer manager and message interpretor for the listener. The interface also behaves somewhat like an iterator where an event object is produced on each request (if available). Part of the reason for this is that a single Netlink read can produce multiple Netlink events.

As the example in the source below demonstrates. The Netlink data buffer: pkt is cast as an nlmsghdr message, with netlink macros NLMSG_OK, and NLMSG_NEXT providing help in moving through the Netlink events found in buffer pkt.


const struct nlmsghdr* mh;
for (mh = reinterpret_cast(pkt);
     NLMSG_OK(mh, ps);
     mh = NLMSG_NEXT(const_cast(mh), ps)) {
        parse_msg(mh);
  }


Inside this loop control of a single nlmsghdr data structure is passed to parse_msg() for further netlink interpretation fun.


The parse_msg() pulls data from the nlmsghdr data structure but this data is dependent on the type of message received. For this implementation we are only interested in 4 types of Netlink messages: RTM_NEWLINK, RTM_DELLINK, RTM_NEWADDR, RTM_DELADDR.


switch (nlHdr->nlmsg_type) {
  case RTM_NEWLINK:
  case RTM_DELLINK:
  case RTM_NEWADDR:
  case RTM_DELADDR:


From this point parse_msg() does the dirty work of pulling specific values from the netlink data structure. This implementation doesn't access the full set of data made available for the interface (only the portions I was interested in cracking open).


Once the relevant bits of data have been parsed, a NetlinkEvent object is created via the constructor (see below). And the event object is then pushed into an stl vector collection. The collection holds onto the event object until it is popped off via a request from the listener object. Processing is done until parse() has iterated through all of the netlink messages and each NetlinkEvent object has been created.


NetlinkEvent e(nlHdr->nlmsg_type,
                 iface,
                 mtu,
                 mac,
                 enabled,
                 running,
                 addr,
                 broadcast,
                 mask_len,
                 index);
  
e.set_ifinfomsg(ifInfo);
_coll.push_back(e);


The event object itself just contains accessors for data, and stream manipulators for ease of dumping data from the object.


Now, that's essentially it. The main entry point is our next and last stop (but of course main can be replaced by library call(s)).


Main


The main entry point just declares a sender and listener object. And initializes the listener, and as a bonus returns the socket back. Normally, the sock should be encapsulated in the listener object, but in this case the socket is reused by the sender since the sender only has a limited use in this implementation. Work to do on my part would be to wrap this in an object so that it is not directly exposed.


NetlinkSend nl_send;
NetlinkListener nl_listener;

int sock = nl_listener.init();


And below we see the listener will return a valid event object if TRUE is returned. The initial startup of the code allows for a single send out the sender object, and the goal of this is to request a netlink dump of interface data.


Finally, our familiar netlink message types are seen below. These are used so that we know what type of event we are working with and can formulate our /presentation/printing as necessary.


while (true) {
    //    cout << "test_netlink: now entering listening mode: " << endl;

    NetlinkEvent nl_event;
    if (nl_listener.process(nl_event) == true) {
      if (send_request) {
        if (nl_send.send(sock, RTM_GETADDR) != 0) {
Snippet of presentation below:
if (nl_event.get_type() == RTM_DELLINK || 
          nl_event.get_type() == RTM_NEWLINK) {
        cout << "  type: " << 
          string(nl_event.get_type()==RTM_DELLINK?"DELLINK":"NEWLINK") << endl;
        cout << "  state: " << string(nl_event.is_link_up()?"UP":"DOWN") << endl;
And that's it! Source code follows. main.cc
#include <stdlib.h>
#include <iostream>
#include <stdio.h>
#include <string>

#include <linux/types.h>
#include <sys/socket.h>
#include <linux/rtnetlink.h>
#include "netlink_send.hh"
#include "netlink_listener.hh"
#include "netlink_event.hh"

using namespace std;

/**
 *
 *
 **/
void 
usage()
{
  fprintf(stdout, "-s   start with sending netlink request\n");
  fprintf(stdout, "-h   help message\n");
}

/**
 *
 *
 **/
int 
main(int argc, char* const argv[])
{
  int ch;
  bool send_request = false;
  bool debug = false;
  
  cout << "netlink_main()" << endl;
  
  while ((ch = getopt(argc, argv, "sdh")) != -1) {
    switch (ch) {
    case 's':
      send_request = true;
      break;
    case 'd':
      debug = true;
      break;
    case 'h':
      usage();
      exit(0);
    }
  }  
  
  NetlinkSend nl_send;
  NetlinkListener nl_listener;

  int sock = nl_listener.init();
  if (sock <= 0) {
    cerr << "test_netlink(), bad voodoo. exiting.." << endl;
    exit(1);
  }

  if (send_request) {
    cout << "sending initial netlink request" << endl;
    nl_listener.set_multipart(true);
    if (nl_send.send(sock, RTM_GETLINK) != 0) {
      cerr << "test_netlink(), error sending" << endl;
      exit(1);
    }
  }

  while (true) {
    //    cout << "test_netlink: now entering listening mode: " << endl;

    NetlinkEvent nl_event;
    if (nl_listener.process(nl_event) == true) {
      if (send_request) {
        if (nl_send.send(sock, RTM_GETADDR) != 0) {
          cerr << "test_netlink(), error sending" << endl;
          exit(1);
        }
        send_request = false;
      }
      else {
        nl_listener.set_multipart(false);
      }

      char buf[20];
      sprintf(buf, "%d", nl_event.get_index());
      cout << "results for " << nl_event.get_iface() << 
        "(" << string(buf) << ")" << endl;
      cout << "  running: " << 
        string(nl_event.get_running() ? "yes" : "no") << endl;
      cout << "  enabled: " << 
        string(nl_event.get_enabled() ? "yes" : "no") << endl;
      if (debug) {
        cout << "  ifinfomsg: " << nl_event.get_ifinfomsg() << endl;
      }
      if (nl_event.get_type() == RTM_DELLINK || 
   nl_event.get_type() == RTM_NEWLINK) {
        cout << "  type: " << 
          string(nl_event.get_type()==RTM_DELLINK?"DELLINK":"NEWLINK") << endl;
        cout << "  state: " << string(nl_event.is_link_up()?"UP":"DOWN") << endl;
        sprintf(buf, "%d", nl_event.get_mtu());
        cout << "  mtu: " << string(buf) << endl;
        cout << "  mac: " << nl_event.get_mac_str() << endl;
      }
      else if (nl_event.get_type() == RTM_DELADDR ||
        nl_event.get_type() == RTM_NEWADDR) {
        cout << "  type: " << 
          string(nl_event.get_type()==RTM_DELADDR?"DELADDR":"NEWADDR") << endl;
        cout << "  addr: " << nl_event.get_addr().str().c_str() << endl;
        cout << "  broadcast: " << nl_event.get_broadcast().str().c_str() << endl;
        char buf[20];
        sprintf(buf, "%d", nl_event.get_mask_len());
        cout << "  mask length: " << string(buf) << endl;
      }
      cout << endl;
    }
    else {
      //      cout << "didn't receive a message, sleeping for 1 second" << endl;
      sleep(1);
    }
  }
  exit(0);
}
netlink_event.cc
#include <linux/types.h>
#include <sys/socket.h>
#include <linux/rtnetlink.h>
#include <net/if.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <syslog.h>

#include <iostream>
#include <string>
#include "netlink_event.hh"

using namespace std;

/**
 *
 *
 **/
NetlinkEvent::NetlinkEvent(int type,
      std::string iface,
      int mtu,
      unsigned char *mac,
      bool enabled,
      bool running,
      IPv4 addr,
      IPv4 broadcast,
      int mask_len,
      int index) :
  _type(type),
  _iface(iface),
  _vif(false),
  _mtu(mtu),
  _enabled(enabled),
  _running(running),
  _addr(addr),
  _broadcast(broadcast),
  _mask_len(mask_len),
  _index(index)
{
  memcpy(_mac, mac, 6);
  if (_iface.find(".") != string::npos) {
    _vif = true;
  }
}

/**
 *
 *
 **/
NetlinkEvent::~NetlinkEvent()
{
}

/**
 *
 *
 **/
void
NetlinkEvent::log()
{
  syslog(LOG_USER | LOG_INFO, "NetlinkEvent::log(): type: %d, 
    iface: %s, mtu: %d, addr: %s, bc: %s, mask: %d, index: %d", 
    _type, _iface.c_str(), _mtu, _addr.str().c_str(), 
    _broadcast.str().c_str(), _mask_len, _index);
}

/**
 *
 *
 **/
NetlinkEventManager::NetlinkEventManager()
{

}

/**
 *
 *
 *
 **/
NetlinkEventManager::~NetlinkEventManager()
{
}

/**
 *
 *
 *
 **/
void
NetlinkEventManager::process(unsigned char* pkt, int size)
{
  if (size <= 0) {
    return;
  }

  size_t ps = size_t(size);

  const struct nlmsghdr* mh;
  for (mh = reinterpret_cast(pkt);
       NLMSG_OK(mh, ps);
       mh = NLMSG_NEXT(const_cast(mh), ps)) {
    parse_msg(mh);
  }
}

/**
 *
 *
 **/
bool
NetlinkEventManager::pop(NetlinkEvent &e)
{
  char buf[20];
  sprintf(buf, "%d", _coll.size());
  
  NLEventIter iter = _coll.begin();
  if (iter != _coll.end()) {
    e = *iter;
    _coll.erase(iter);
    return true;
  }
  return false;
}

/**
 *
 *
 *
 **/
void 
NetlinkEventManager::parse_msg(const struct nlmsghdr *nlHdr)
{
  bool enabled;
  bool running;
  string iface;
  int mtu = -1;
  int index = -1;
  unsigned char mac[6];
  IPv4 addr, broadcast;
  int mask_len = -1;

  bzero(mac, 6);

  struct ifinfomsg* ifInfo = (struct ifinfomsg *)NLMSG_DATA(nlHdr);

  //link state flag 
  enabled = ifInfo->ifi_flags & IFF_UP;
  running = ifInfo->ifi_flags & IFF_RUNNING;
  index = ifInfo->ifi_index;

  struct rtattr* rtAttr = (struct rtattr *)IFLA_RTA(ifInfo);
  int rtLen = IFLA_PAYLOAD(nlHdr);

  switch (nlHdr->nlmsg_type) {
  case RTM_NEWLINK:
  case RTM_DELLINK:
  case RTM_NEWADDR:
  case RTM_DELADDR:
    for(;RTA_OK(rtAttr,rtLen);rtAttr = RTA_NEXT(rtAttr,rtLen)){
      if (nlHdr->nlmsg_type == RTM_NEWLINK ||
   nlHdr->nlmsg_type == RTM_DELLINK) {
 switch(rtAttr->rta_type) {
 case IFLA_IFNAME:
   iface = string((char*)RTA_DATA(rtAttr));
   break;
 case IFLA_ADDRESS:
   memcpy(mac, RTA_DATA(rtAttr), 6);
   break;
 case IFLA_MTU:
   mtu = *((unsigned int *)RTA_DATA(rtAttr));
   break;
 default:
   break;
 }
      }
      else if (nlHdr->nlmsg_type == RTM_NEWADDR ||
        nlHdr->nlmsg_type == RTM_DELADDR) {
 uint32_t address;
 struct ifaddrmsg *ifAddrs;
 ifAddrs = (struct ifaddrmsg *)NLMSG_DATA(nlHdr);
 mask_len = ifAddrs->ifa_prefixlen;

 switch(rtAttr->rta_type) {
 case IFA_LOCAL:
   address = *(uint32_t *)RTA_DATA(rtAttr);
   addr = IPv4(address);
   break;
 case IFA_LABEL:
   iface = string((char*)RTA_DATA(rtAttr));
   break;
 case IFA_BROADCAST:
   address = *(uint32_t *)RTA_DATA(rtAttr);
   broadcast = IPv4(address);
   break;
 default:
   break;
 }
      }
    }
    {
      NetlinkEvent e(nlHdr->nlmsg_type,
       iface,
       mtu,
       mac,
       enabled,
       running,
       addr,
       broadcast,
       mask_len,
       index);

      e.set_ifinfomsg(ifInfo);
      
      e.log();
      _coll.push_back(e);
    }
    break;
  case NLMSG_ERROR:
      cerr << "netlink message of type ERROR received" << endl;
    break;
  case NLMSG_DONE:
      cerr << "netlink message of type DONE received" << endl;
    break;
  case NLMSG_NOOP:
      cerr << "netlink message of type NOOP received" << endl;
    break;
  default:
      cerr << "unknown netlink message type received" << endl;
    break;
  }
}


/**
 *
 *
 *
 **/
typedef struct {
  unsigned int iff_flag;
  char *name;
} iff_flags_name;


string
NetlinkEvent::get_ifinfomsg()
{
  string ret;
  char buf[40];

  sprintf(buf, "%uc", _ifinfo.ifi_family);
  ret = "ifi_family: " + string(buf) + ", ";
  sprintf(buf, "%us", _ifinfo.ifi_type);
  ret += "ifi_type: " + string(buf) + ", ";
  sprintf(buf, "%d", _ifinfo.ifi_index);
  ret += "ifi_index: " + string(buf) + ", ";
  sprintf(buf, "%ud", _ifinfo.ifi_flags);
  ret += "ifi_flags: " + string(buf) + ", ";
  sprintf(buf, "%ud", _ifinfo.ifi_change);
  ret += "ifi_change: " + string(buf);
  return ret;
}

/*
string
NetlinkEvent::operator<<(const ostream &o)
{
    UNUSED(o);
  return ("");
}
*/
std::ostream & operator <<(std::ostream & Stream, const NetlinkEvent & instance) 
{ 
  //  Stream << ... fields from instance 
  const NetlinkEvent foo = instance;
  return Stream; 
}

netlink_listener.cc
#include <errno.h>
#include <linux/types.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/rtnetlink.h>

#include <vector>
#include <string>
#include <iostream>

#include "netlink_event.hh"
#include "netlink_listener.hh"


using namespace std;


#define SO_RCV_BUF_SIZE_MAX      (256*1024) // Desired socket buffer size
#define SO_RCV_BUF_SIZE_MIN      (48*1024)  // Min. socket buffer size
#define NLSOCK_BYTES  (8*1024)

/**
 *
 *
 **/
NetlinkListener::NetlinkListener() : 
  _fd(-1),
  _is_multipart_message_read(false)
{

}

/**
 *
 *
 **/
NetlinkListener::~NetlinkListener()
{
  close(_fd);
}

/**
 *
 *
 **/
int
NetlinkListener::init()
{
  struct sockaddr_nl snl;
  socklen_t  snl_len;
  
  if (_fd >= 0) {
    cerr << "socket cannot be initialized" << endl;
    return _fd;
  }
  
  _fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
  if (_fd < 0) {
    cerr << string("Could not open netlink socket: ") <<  strerror(errno) << endl;
    return _fd;
  }
  
  comm_sock_set_rcvbuf(_fd, SO_RCV_BUF_SIZE_MAX, SO_RCV_BUF_SIZE_MIN);
  
  memset(&snl, 0, sizeof(snl));
  snl.nl_family = AF_NETLINK;
  snl.nl_pid    = getpid();  // Let the kernel assign the pid to the socket
  snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;//_nl_groups;
  if (bind(_fd, reinterpret_cast(&snl), sizeof(snl)) < 0) {
    cerr << string("bind(AF_NETLINK) failed: ") << strerror(errno) << endl;
    close(_fd);
    _fd = -1;
    return _fd;
  }
  
  snl_len = sizeof(snl);
  if (getsockname(_fd, reinterpret_cast(&snl), &snl_len) < 0) {
    cerr << string("getsockname(AF_NETLINK) failed: ") << strerror(errno) << endl;
    close(_fd);
    _fd = -1;
    return _fd;
  }
  if (snl_len != sizeof(snl)) {
    cerr << string("Wrong address length of AF_NETLINK socket: ") << endl;
    close(_fd);
    _fd = -1;
    return _fd;
  }
  if (snl.nl_family != AF_NETLINK) {
    cerr << string("Wrong address family of AF_NETLINK socket: ") << endl;
    close(_fd);
    _fd = -1;
    return _fd;
  }
  return _fd;
}

/**
 *
 *
 **/
bool
NetlinkListener::process(NetlinkEvent &e)
{
  if (_fd <= 0) {
    return false;
  }

  vector message;
  vector buffer(NLSOCK_BYTES);
  size_t last_mh_off = 0;
  size_t off = 0;
  ssize_t got = -1;

    char buf[20];

  for ( ; ; ) {
    //don't block on recv
    do {
      got = recv(_fd, &buffer[0], buffer.size(), MSG_DONTWAIT | MSG_PEEK);
      if ((got < 0) && (errno == EINTR))
 continue; // XXX: the receive was interrupted by a signal
      if ((got < 0) || (got < (ssize_t)buffer.size()))
 break;  // The buffer is big enough
      buffer.resize(buffer.size() + NLSOCK_BYTES);
    } while (true);
    
    got = recv(_fd, &buffer[0], buffer.size(),
        MSG_DONTWAIT);
    //    got = read(_fd, &buffer[0], buffer.size());
    if (got < 0) {
      if (errno == EINTR)
 continue;
      //      cerr << "Netlink socket read error: " << endl;
      break;
    }

    message.resize(message.size() + got);
    memcpy(&message[off], &buffer[0], got);
    off += got;
    
    if ((off - last_mh_off) < (ssize_t)sizeof(struct nlmsghdr)) {
      cerr << string("Netlink socket recvfrom failed: message truncated: ") << endl;
      break;
    }
    
    //
    // If this is a multipart message, it must be terminated by NLMSG_DONE
    //

    bool is_end_of_message = false;
    size_t new_size = off - last_mh_off;
    const struct nlmsghdr* mh;
    for (mh = reinterpret_cast(&buffer[last_mh_off]);
  NLMSG_OK(mh, new_size);
  mh = NLMSG_NEXT(const_cast(mh), new_size)) {
      if ((mh->nlmsg_flags & NLM_F_MULTI)
   || _is_multipart_message_read) {
 sprintf(buf, "%d", mh->nlmsg_type);
 is_end_of_message = false;
 if (mh->nlmsg_type == NLMSG_DONE) {
   is_end_of_message = true;
 }
      }
    }
    last_mh_off = reinterpret_cast(mh) - reinterpret_cast(&buffer[0]);
    if (is_end_of_message) {
      break;
    }
  }

  _nl_event_mgr.process(&message[0], off);
  return _nl_event_mgr.pop(e);
}

/**
 *
 *
 **/
int
NetlinkListener::comm_sock_set_rcvbuf(int sock, int desired_bufsize, int min_bufsize)
{
    int delta = desired_bufsize / 2;

    /*
     * Set the socket buffer size.  If we can't set it as large as we
     * want, search around to try to find the highest acceptable
     * value.  The highest acceptable value being smaller than
     * minsize is a fatal error.
     */
    if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
     &desired_bufsize,
     sizeof(desired_bufsize)) < 0) {
 desired_bufsize -= delta;
 while (1) {
     if (delta > 1)
  delta /= 2;

     if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
      &desired_bufsize,
      sizeof(desired_bufsize)) < 0) {
      desired_bufsize -= delta;
      if (desired_bufsize <= 0)
        break;
      } 
      else {
        if (delta < 1024) {
          break;
        }
        desired_bufsize += delta;
     }
 }
 if (desired_bufsize < min_bufsize) {
   cerr << "Cannot set receiving buffer size of socket" << endl;
   return -1;
 }
    }

    return (desired_bufsize);
}
netlink_send.cc
#include <errno.h>
#include <string.h>
#include <linux/types.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <linux/rtnetlink.h>

#include <vector>
#include <string>
#include <iostream>

#include "netlink_send.hh"

using namespace std;

NetlinkSend::NetlinkSend()
{
}


NetlinkSend::~NetlinkSend()
{
}

int
NetlinkSend::send(int sock, int type)
{
  int ret;
  struct sockaddr_nl snl;

  struct
  {
    struct nlmsghdr nlh;
    struct rtgenmsg g;
  } req;


  /* Check netlink socket. */
  if (sock < 0) {
    cout << "sock is not active, exiting" << endl;
    return -1;
  }

  memset (&snl, 0, sizeof snl);
  snl.nl_family = AF_NETLINK;

  req.nlh.nlmsg_len = sizeof req;
  req.nlh.nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
  req.nlh.nlmsg_pid = getpid();
  req.nlh.nlmsg_type = type;
  req.nlh.nlmsg_seq = time(NULL);
  req.g.rtgen_family = AF_UNSPEC;
 
  ret = sendto (sock, (void*) &req, sizeof req, 0, 
  (struct sockaddr*) &snl, sizeof snl);
  if (ret < 0) {
    cout << "netlink_send failed on send " << endl;
    return -1;
  }
  return 0;
}
netlink_event.hh
#ifndef __NETLINK_EVENT_HH__
#define __NETLINK_EVENT_HH__

#include <string.h>
#include <string>
#include <vector>
#include <ostream>
#include <linux/rtnetlink.h>
#include <stdio.h>
#include <net/if.h>
#include "netlink_types.hh"


/**
 *
 *
 **/
class NetlinkEvent
{
public:
  NetlinkEvent(int type,
        std::string iface,
        int mtu,
        unsigned char *mac,
        bool enabled,
        bool running,
        IPv4 addr,
        IPv4 broadcast,
        int mask_len,
        int index);

  NetlinkEvent() :
  _type(-1),
  _mtu(-1),
  _enabled(false),
  _running(false),
  _mask_len(-1),
  _index(-1) {}

  ~NetlinkEvent();


  std::string 
  get_iface() const {return _iface;}
  
  int 
  get_mtu() const {return _mtu;}

  std::string
  get_mac_str() const {
    char buf[18];
      sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X",
   _mac[0], _mac[1], _mac[2],
   _mac[3], _mac[4], _mac[5]
   );
    return std::string(buf);
  }

  int
  get_type() const {return _type;}
  
  bool
  get_enabled() const {return _enabled;}

  bool
  get_running() const {return _running;}

  IPv4
  get_addr() const {return _addr;}
  
  IPv4
  get_broadcast() const {return _broadcast;}

  int
  get_mask_len() const {return _mask_len;}

  bool
  is_link_up() const {return _enabled;}

  int
  get_index() const {return _index;}

  bool 
  is_vif() const {return _vif;}

  void
  log();

  void
  set_ifinfomsg(ifinfomsg *ifinfo) {_ifinfo = *ifinfo;}

  std::string
  get_ifinfomsg();

private:
  int _type;
  std::string _iface;
  bool _vif;
  int _mtu;
  unsigned char _mac[6];
  bool _enabled;
  bool _running;
  IPv4 _addr;
  IPv4 _broadcast;
  int _mask_len;
  int _index;

  //debug
  struct ifinfomsg _ifinfo;
};



/**
 *
 *
 **/
class NetlinkEventManager
{
public:
  typedef std::vector NLEventColl;
  typedef std::vector::iterator NLEventIter;

public: //methods
  friend std::ostream & operator<< (std::ostream &, const NetlinkEvent &);

  NetlinkEventManager();

  ~NetlinkEventManager();

  void 
  process(unsigned char *pkt, int size);

  bool
  pop(NetlinkEvent &e);

  

private: //methods
  void
  parse_msg(const struct nlmsghdr*);

private: //variables
  NLEventColl _coll;
};


#endif // __NETLINK_EVENT_HH__

netlink_listener.hh
#ifndef __NETLINK_LISTENER_HH__
#define __NETLINK_LISTENER_HH__

#include "netlink_event.hh"
#include 

class NetlinkListener
{
public: //methods
  NetlinkListener();
  ~NetlinkListener();

  /*
   * returns socket fd
   */
  int
  init();

  bool
  process(NetlinkEvent &e);

  int
  get_sock() {return _fd;}

  void
  set_multipart(bool state) {_is_multipart_message_read = state;}

private: //methods
int
comm_sock_set_rcvbuf(int sock, int desired_bufsize, int min_bufsize);

private: //vraiables
  int _fd;
  bool _is_multipart_message_read;
  NetlinkEventManager _nl_event_mgr;
};

#endif //__NETLINK_LISTENER_HH__
netlink_send.hh
#ifndef __NETLINK_SEND_HH__
#define __NETLINK_SEND_HH__

class NetlinkSend
{
public:
  NetlinkSend();
  ~NetlinkSend();
  
  int
  send(int sock, int type);
};

#endif //__NETLINK_SEND_HH__
netlink_types.hh
#ifndef __NETLINK_TYPES_HH__
#define __NETLINK_TYPES_HH__

#include <stdint.h>
#include <sys/types.h>
#include <net/ethernet.h>
#include <netinet/ether.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


class IPv4
{
public:
  IPv4() : _addr(0) {}
  IPv4(uint32_t addr) : _addr(addr) {}

  std::string
  str() {
    struct in_addr a;
    a.s_addr = _addr;
    return std::string(inet_ntoa(a));
  }
  
private:
  uint32_t _addr;
};

class Mac
{
public:
  Mac() {}
  Mac(struct ether_addr mac) {
    _mac = mac;
  }

private:
  struct ether_addr _mac;

};




#endif //__NETLINK_TYPES_HH__


4 comments:

  1. Hi Michael,

    great work!
    I was wondering if you forgot to mention the part of
    #include "netlink_types.hh".
    It is included in netlink_event.hh. Can you publish the missing part.

    Thanks in advance

    Phil

    ReplyDelete
    Replies
    1. Thanks Phil--added the missing header. Thanks for the ping--also fixed up the include statements so that the html processor wasn't eating the angle brackets too.

      Mike

      Delete
    2. hi if we have an interface eth0 and its alias eth0:1 is there any way to know which ip is for which eth0

      Delete
    3. The nlmsg structure will contain different IDs per interface (actual and vif)--you can see this below:

      # ip add show dev eth0.123
      7: eth0.123@eth0: mtu 1500 qdisc noqueue state UP
      link/ether 08:00:27:14:53:0d brd ff:ff:ff:ff:ff:ff
      inet6 fe80::a00:27ff:fe14:530d/64 scope link
      valid_lft forever preferred_lft forever
      [edit]
      # ip add show dev eth0
      2: eth0: mtu 1500 qdisc pfifo_fast state UP qlen 1000
      link/ether 08:00:27:14:53:0d brd ff:ff:ff:ff:ff:ff
      inet 10.3.0.146/24 brd 10.3.0.255 scope global eth0
      inet6 fe80::a00:27ff:fe14:530d/64 scope link
      valid_lft forever preferred_lft forever

      Delete