netlink address get/new/del
This commit is contained in:
parent
364d03f15c
commit
d891ad5a48
3
Makefile
3
Makefile
@ -17,7 +17,8 @@ OBJS = \
|
|||||||
ip4.o \
|
ip4.o \
|
||||||
packet.o \
|
packet.o \
|
||||||
udp4.o \
|
udp4.o \
|
||||||
eth.o
|
eth.o \
|
||||||
|
rtnetlink.o
|
||||||
|
|
||||||
all: client test2
|
all: client test2
|
||||||
|
|
||||||
|
|||||||
18
dhcp4.cpp
18
dhcp4.cpp
@ -1,4 +1,5 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
@ -54,6 +55,23 @@ namespace dhcp4 {
|
|||||||
return (sizeof (message)) + index;
|
return (sizeof (message)) + index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char const * message_type_tostr(int message_type)
|
||||||
|
{
|
||||||
|
switch (message_type) {
|
||||||
|
case message_type::dhcpdiscover: return "dhcpdiscover";
|
||||||
|
case message_type::dhcpoffer: return "dhcpoffer";
|
||||||
|
case message_type::dhcprequest: return "dhcprequest";
|
||||||
|
case message_type::dhcpdecline: return "dhcpdecline";
|
||||||
|
case message_type::dhcpack: return "dhcpack";
|
||||||
|
case message_type::dhcpnak: return "dhcpnak";
|
||||||
|
case message_type::dhcprelease: return "dhcprelease";
|
||||||
|
case message_type::dhcpinform: return "dhcpinform";
|
||||||
|
default:
|
||||||
|
assert(false);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void parse_options(message const * m, int options_length, options * o)
|
void parse_options(message const * m, int options_length, options * o)
|
||||||
{
|
{
|
||||||
uint8_t const * options = m->options;
|
uint8_t const * options = m->options;
|
||||||
|
|||||||
2
dhcp4.h
2
dhcp4.h
@ -80,5 +80,7 @@ namespace dhcp4 {
|
|||||||
uint8_t const * chaddr,
|
uint8_t const * chaddr,
|
||||||
uint32_t dhcp_message_type);
|
uint32_t dhcp_message_type);
|
||||||
|
|
||||||
|
char const * message_type_tostr(int message_type);
|
||||||
|
|
||||||
void parse_options(message const * m, int options_length, options * o);
|
void parse_options(message const * m, int options_length, options * o);
|
||||||
}
|
}
|
||||||
|
|||||||
107
netlink.cpp
Normal file
107
netlink.cpp
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
#include <linux/rtnetlink.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
|
||||||
|
#include "ip4.h"
|
||||||
|
#include "rtnetlink.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
|
||||||
|
if (fd < 0) {
|
||||||
|
perror("socket");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_nl bsa = { .nl_family = AF_NETLINK };
|
||||||
|
ret = bind(fd, (struct sockaddr *)&bsa, (sizeof (sockaddr_nl)));
|
||||||
|
if (ret < 0) {
|
||||||
|
perror("bind");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_nl gsa;
|
||||||
|
socklen_t addrlen = (sizeof (sockaddr_nl));
|
||||||
|
ret = getsockname(fd, (struct sockaddr *)&gsa, &addrlen);
|
||||||
|
if (ret < 0) {
|
||||||
|
perror("getsockname");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// interface index
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
char const * interface_name = "wlp174s0";
|
||||||
|
struct ifreq if_index = {};
|
||||||
|
strncpy(if_index.ifr_name, interface_name, IFNAMSIZ - 1);
|
||||||
|
int iret = ioctl(fd, SIOCGIFINDEX, &if_index);
|
||||||
|
if (iret < 0) {
|
||||||
|
perror("ioctl(SIOCGIFINDEX)");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
printf("ifa_index %d\n", if_index.ifr_ifindex);
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
// message
|
||||||
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
int rtrets = rtnetlink::send_getaddr(fd, if_index.ifr_ifindex);
|
||||||
|
if (rtrets < 0) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int out_length = 64;
|
||||||
|
rtnetlink::newaddr out[out_length];
|
||||||
|
int rtreth = rtnetlink::handle_getaddr(fd, if_index.ifr_ifindex, out, out_length);
|
||||||
|
if (rtreth < 0) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < rtreth; i++) {
|
||||||
|
printf("out[%d] prefixlen %d\n", i, out[i].ifa_prefixlen);
|
||||||
|
printf("out[%d] local %s\n", i, ip4::tostr(out[i].local));
|
||||||
|
printf("out[%d] address %s\n", i, ip4::tostr(out[i].address));
|
||||||
|
|
||||||
|
// del
|
||||||
|
{
|
||||||
|
printf("del\n");
|
||||||
|
int dret = rtnetlink::send_typeaddr(fd, RTM_DELADDR, if_index.ifr_ifindex,
|
||||||
|
out[i].ifa_prefixlen, out[i].local, out[i].address);
|
||||||
|
if (dret < 0) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
int eret = rtnetlink::handle_error(fd);
|
||||||
|
if (eret < 0) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// new
|
||||||
|
{
|
||||||
|
printf("new\n");
|
||||||
|
int dret = rtnetlink::send_typeaddr(fd, RTM_NEWADDR, if_index.ifr_ifindex,
|
||||||
|
out[i].ifa_prefixlen, out[i].local, out[i].address);
|
||||||
|
if (dret < 0) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
int eret = rtnetlink::handle_error(fd);
|
||||||
|
if (eret < 0) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
263
rtnetlink.cpp
Normal file
263
rtnetlink.cpp
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
#include <linux/rtnetlink.h>
|
||||||
|
|
||||||
|
#include "rtnetlink.h"
|
||||||
|
|
||||||
|
static inline int align4(int len)
|
||||||
|
{
|
||||||
|
return (len + 3) & (~3);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct newaddr_msg {
|
||||||
|
struct nlmsghdr nl;
|
||||||
|
struct ifaddrmsg ifaddr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct deladdr_msg {
|
||||||
|
struct nlmsghdr nl;
|
||||||
|
struct ifaddrmsg ifaddr;
|
||||||
|
struct {
|
||||||
|
struct rtattr rtattr;
|
||||||
|
uint32_t value;
|
||||||
|
} attr[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace rtnetlink {
|
||||||
|
|
||||||
|
bool parse_newaddr(uint8_t const * buf, ssize_t length,
|
||||||
|
uint32_t ifa_index,
|
||||||
|
newaddr * out, int out_length,
|
||||||
|
int & index)
|
||||||
|
{
|
||||||
|
uint8_t const * bufi = buf;
|
||||||
|
|
||||||
|
while (index < out_length) {
|
||||||
|
struct nlmsghdr * nl = (struct nlmsghdr *)&bufi[0];
|
||||||
|
struct ifaddrmsg * ifaddr = (struct ifaddrmsg *)&bufi[(sizeof (struct nlmsghdr))];
|
||||||
|
size_t rtattr_offset = (sizeof (struct nlmsghdr)) + (sizeof (struct ifaddrmsg));
|
||||||
|
|
||||||
|
assert((nl->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI);
|
||||||
|
|
||||||
|
if (nl->nlmsg_type == NLMSG_DONE) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(nl->nlmsg_type == RTM_NEWADDR);
|
||||||
|
|
||||||
|
out[index].ifa_prefixlen = ifaddr->ifa_prefixlen;
|
||||||
|
|
||||||
|
while (rtattr_offset < nl->nlmsg_len) {
|
||||||
|
struct rtattr * rtattr = (struct rtattr *)&bufi[rtattr_offset];
|
||||||
|
rtattr_offset += align4(rtattr->rta_len);
|
||||||
|
|
||||||
|
void * value = (void *)(((ptrdiff_t)rtattr) + (sizeof (struct rtattr)));
|
||||||
|
|
||||||
|
switch (rtattr->rta_type) {
|
||||||
|
case IFA_ADDRESS:
|
||||||
|
out[index].address = *((uint32_t *)value);
|
||||||
|
break;
|
||||||
|
case IFA_LOCAL:
|
||||||
|
out[index].local = *((uint32_t *)value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ifaddr->ifa_index == ifa_index) {
|
||||||
|
index += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bufi = &bufi[nl->nlmsg_len];
|
||||||
|
if (bufi - buf == length) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_getaddr(int fd, uint32_t ifa_index, newaddr * out, int out_length)
|
||||||
|
{
|
||||||
|
bool done = false;
|
||||||
|
int index = 0;
|
||||||
|
|
||||||
|
while (!done) {
|
||||||
|
struct sockaddr_nl addr = { .nl_family = AF_NETLINK };
|
||||||
|
struct iovec iov[1] = {};
|
||||||
|
struct msghdr msg = {
|
||||||
|
.msg_name = (void *)&addr,
|
||||||
|
.msg_namelen = (sizeof (sockaddr_nl)),
|
||||||
|
.msg_iov = iov,
|
||||||
|
.msg_iovlen = 1,
|
||||||
|
.msg_flags = MSG_TRUNC,
|
||||||
|
};
|
||||||
|
ssize_t peek_length = recvmsg(fd, &msg, MSG_PEEK | MSG_TRUNC);
|
||||||
|
if (peek_length < 0) {
|
||||||
|
perror("recvmsg(MSG_PEEK|MSG_TRUNC)");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t * buf = (uint8_t *)malloc(peek_length);
|
||||||
|
|
||||||
|
iov[0].iov_base = buf;
|
||||||
|
iov[0].iov_len = peek_length;
|
||||||
|
msg.msg_flags = 0;
|
||||||
|
ssize_t length = recvmsg(fd, &msg, 0);
|
||||||
|
if (length < 0) {
|
||||||
|
perror("recvmsg");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
assert(peek_length == length);
|
||||||
|
|
||||||
|
done = parse_newaddr(buf, length, ifa_index, out, out_length, index);
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
int send_getaddr(int fd, uint32_t ifa_index)
|
||||||
|
{
|
||||||
|
const size_t message_length = (sizeof (struct newaddr_msg));
|
||||||
|
|
||||||
|
struct newaddr_msg rtmmsg = {
|
||||||
|
.nl = {
|
||||||
|
.nlmsg_len = message_length,
|
||||||
|
.nlmsg_type = RTM_GETADDR,
|
||||||
|
.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
|
||||||
|
.nlmsg_seq = 1234,
|
||||||
|
},
|
||||||
|
.ifaddr = {
|
||||||
|
.ifa_family = AF_INET,
|
||||||
|
.ifa_scope = RT_SCOPE_UNIVERSE,
|
||||||
|
.ifa_index = ifa_index,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct iovec iov[1] = {
|
||||||
|
{
|
||||||
|
.iov_base = &rtmmsg,
|
||||||
|
.iov_len = message_length,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct sockaddr_nl addr = { .nl_family = AF_NETLINK };
|
||||||
|
struct msghdr msg = {
|
||||||
|
.msg_name = (void *)&addr,
|
||||||
|
.msg_namelen = (sizeof (sockaddr_nl)),
|
||||||
|
.msg_iov = iov,
|
||||||
|
.msg_iovlen = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
ssize_t sret = sendmsg(fd, &msg, 0);
|
||||||
|
if (sret < 0) {
|
||||||
|
perror("sendmsg");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int send_typeaddr(int fd, uint16_t nlmsg_type, uint32_t ifa_index, uint8_t ifa_prefixlen, uint32_t local, uint32_t address)
|
||||||
|
{
|
||||||
|
const size_t message_length = (sizeof (struct deladdr_msg));
|
||||||
|
|
||||||
|
struct deladdr_msg rtmmsg = {
|
||||||
|
.nl = {
|
||||||
|
.nlmsg_len = message_length,
|
||||||
|
.nlmsg_type = nlmsg_type,
|
||||||
|
.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
|
||||||
|
.nlmsg_seq = 12345,
|
||||||
|
},
|
||||||
|
.ifaddr = {
|
||||||
|
.ifa_family = AF_INET,
|
||||||
|
.ifa_prefixlen = ifa_prefixlen,
|
||||||
|
.ifa_scope = RT_SCOPE_UNIVERSE,
|
||||||
|
.ifa_index = ifa_index,
|
||||||
|
},
|
||||||
|
.attr = {
|
||||||
|
{
|
||||||
|
.rtattr = { .rta_len = 8, .rta_type = IFA_LOCAL },
|
||||||
|
.value = local,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.rtattr = { .rta_len = 8, .rta_type = IFA_ADDRESS },
|
||||||
|
.value = address,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct iovec iov[1] = {
|
||||||
|
{
|
||||||
|
.iov_base = &rtmmsg,
|
||||||
|
.iov_len = message_length,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
struct sockaddr_nl addr = { .nl_family = AF_NETLINK };
|
||||||
|
struct msghdr msg = {
|
||||||
|
.msg_name = (void *)&addr,
|
||||||
|
.msg_namelen = (sizeof (sockaddr_nl)),
|
||||||
|
.msg_iov = iov,
|
||||||
|
.msg_iovlen = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
ssize_t sret = sendmsg(fd, &msg, 0);
|
||||||
|
if (sret < 0) {
|
||||||
|
perror("sendmsg");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int handle_error(int fd)
|
||||||
|
{
|
||||||
|
struct sockaddr_nl addr = { .nl_family = AF_NETLINK };
|
||||||
|
struct iovec iov[1] = {};
|
||||||
|
struct msghdr msg = {
|
||||||
|
.msg_name = (void *)&addr,
|
||||||
|
.msg_namelen = (sizeof (sockaddr_nl)),
|
||||||
|
.msg_iov = iov,
|
||||||
|
.msg_iovlen = 1,
|
||||||
|
.msg_flags = MSG_TRUNC,
|
||||||
|
};
|
||||||
|
ssize_t peek_length = recvmsg(fd, &msg, MSG_PEEK | MSG_TRUNC);
|
||||||
|
if (peek_length < 0) {
|
||||||
|
perror("recvmsg(MSG_PEEK|MSG_TRUNC)");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t * buf = (uint8_t *)malloc(peek_length);
|
||||||
|
|
||||||
|
iov[0].iov_base = buf;
|
||||||
|
iov[0].iov_len = peek_length;
|
||||||
|
msg.msg_flags = 0;
|
||||||
|
ssize_t length = recvmsg(fd, &msg, 0);
|
||||||
|
if (length < 0) {
|
||||||
|
perror("recvmsg");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
assert(peek_length == length);
|
||||||
|
|
||||||
|
struct nlmsghdr * nl = (struct nlmsghdr *)&buf[0];
|
||||||
|
struct nlmsgerr * msgerr = (struct nlmsgerr *)&buf[(sizeof (struct nlmsghdr))];
|
||||||
|
|
||||||
|
assert(nl->nlmsg_type == NLMSG_ERROR);
|
||||||
|
if (msgerr->error < 0) {
|
||||||
|
printf("msgerr %d %s\n", msgerr->error, strerror(-msgerr->error));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
23
rtnetlink.h
Normal file
23
rtnetlink.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace rtnetlink {
|
||||||
|
|
||||||
|
struct newaddr {
|
||||||
|
uint32_t address;
|
||||||
|
uint32_t local;
|
||||||
|
uint32_t ifa_prefixlen;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool parse_newaddr(uint8_t const * buf, ssize_t length,
|
||||||
|
uint32_t ifa_index,
|
||||||
|
newaddr * out, int out_length,
|
||||||
|
int & index);
|
||||||
|
|
||||||
|
int handle_getaddr(int fd, uint32_t ifa_index, newaddr * out, int out_length);
|
||||||
|
int send_getaddr(int fd, uint32_t ifa_index);
|
||||||
|
|
||||||
|
int send_typeaddr(int fd, uint16_t nlmsg_type, uint32_t ifa_index, uint8_t ifa_prefixlen, uint32_t local, uint32_t address);
|
||||||
|
int handle_error(int fd);
|
||||||
|
}
|
||||||
@ -19,7 +19,7 @@ void parse_packet(uint8_t const * buf, int length)
|
|||||||
int dhcp4_options_length = length - (packet::dhcp4_offset + (offsetof (dhcp4::message, options)));
|
int dhcp4_options_length = length - (packet::dhcp4_offset + (offsetof (dhcp4::message, options)));
|
||||||
dhcp4::options options = {};
|
dhcp4::options options = {};
|
||||||
dhcp4::parse_options(p.dhcp4, dhcp4_options_length, &options);
|
dhcp4::parse_options(p.dhcp4, dhcp4_options_length, &options);
|
||||||
printf("message type: %d\n", options.message_type);
|
printf("message type: %s\n", dhcp4::message_type_tostr(options.message_type));
|
||||||
printf("subnet mask: %s\n", ip4::tostr(options.subnet_mask));
|
printf("subnet mask: %s\n", ip4::tostr(options.subnet_mask));
|
||||||
printf("router: %s\n", ip4::tostr(options.router));
|
printf("router: %s\n", ip4::tostr(options.router));
|
||||||
printf("domain name server: %s\n", ip4::tostr(options.domain_name_server));
|
printf("domain name server: %s\n", ip4::tostr(options.domain_name_server));
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user