2

I am trying to add an IPv6 address to an ethernet interface, using libmnl. After constructing a message and sending to the kernel, I saw that it was not added to the interface, even though the return codes for the kernel reply did not contain any error. Kindly can anybody have a look, and help me correct it. Am I supposed to add more attributes to the nlmsghdr or something else?

#include <assert.h>
#include <string.h>
#include <inttypes.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <libmnl/libmnl.h>
#include <linux/rtnetlink.h>
#include <net/if.h>
#include <time.h>
static struct mnl_socket *nl;
static unsigned int nlportid;

int mnl_init(void);
int mnl_init(){
    nl = mnl_socket_open(NETLINK_ROUTE);
    if(nl == NULL){
        printf("Error: mnl_socket_open\n");
        return 0;
    }
    if(mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0){
        printf("Error: mnl_socket_bind\n");
        return 0;
    }
    nlportid = mnl_socket_get_portid(nl);
    return 1;
}
int add_to_interface(const char* eip){
    char buf[MNL_SOCKET_BUFFER_SIZE];
    struct nlmsghdr *nlh;
    struct ifaddrmsg *ifm;
    int ret;
    uint8_t seq;

    nlh = mnl_nlmsg_put_header(buf);

    nlh->nlmsg_type = RTM_NEWADDR;
    nlh->nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL;
    nlh->nlmsg_seq = seq = time(NULL);

    ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));

    ifm->ifa_family = AF_INET6;
    ifm->ifa_prefixlen = 64; 
    ifm->ifa_flags = IFA_F_PERMANENT;
    ifm->ifa_scope = RT_SCOPE_UNIVERSE;
    /* TODO get interaface name from user or configuration*/
    ifm->ifa_index = if_nametoindex("eth0");
    unsigned char eipn[16]; 
    inet_pton(AF_INET6, eip, eipn);
    mnl_attr_put(nlh, IFA_ADDRESS, 16,eipn);

    mnl_nlmsg_fprintf(stdout, nlh, nlh->nlmsg_len, sizeof(struct ifaddrmsg));

    if(mnl_socket_sendto(nl,nlh, nlh->nlmsg_len) < 0){
        printf("Error: mnl_socket_sendto");
        return 0;
    }
    ret = mnl_socket_recvfrom(nl,buf, sizeof(buf));
    if(ret == -1){
        printf("Error: mnl_socket_recvfrom");
        return 0;
    }
    ret = mnl_cb_run(buf, ret, seq, nlportid, NULL, NULL); 
    return 0;
}

int main(int argc, char *argv[]){
    if(mnl_init()){
        add_to_interface("2001::20c:29ff:fe5f:13c7/64"); // for testing
    }
}
Khawar
  • 23
  • 4
  • 1
    FYI I added example for both IPv4 and IPv6 in libmnl: https://git.netfilter.org/libmnl/tree/examples/rtnl/rtnl-addr-add.c – pevik May 07 '19 at 07:48

2 Answers2

0

I don't really know anything about libmnl, and the following solution is not perfect. But in case you're still stuck...

Notice that you're dropping several error codes. This one, in particular:

inet_pton(AF_INET6, eip, eipn);

Should be something in the lines of this:

ret = inet_pton(AF_INET6, eip, eipn);
if (ret != 1) {
    printf("Bad address.\n");
    return -22;
}

And I suppose you can tell where I'm going with this. This:

add_to_interface("2001::20c:29ff:fe5f:13c7/64"); // for testing

Should be this:

add_to_interface("2001::20c:29ff:fe5f:13c7"); // for testing

That fixes it for me. Except it hags at mnl_socket_recvfrom() because the kernel does not answer, apparently.

But do be more careful with those error codes; the ones I mentioned aren't the only ones.

Yd Ahhrk
  • 1,088
  • 12
  • 24
0

Solution from Pablo Neira,

eipn should be 'struct in6_addr' instead.

    mnl_attr_put(nlh, IFA_ADDRESS, 16,eipn);

So this looks like:

      mnl_attr_put(nlh, IFA_ADDRESS, sizeof(eipn), &eipn);
Khawar
  • 23
  • 4