Skip to content

Commit 5b58182

Browse files
committed
added get_local_ips command
1 parent c372541 commit 5b58182

File tree

6 files changed

+239
-0
lines changed

6 files changed

+239
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
obj/
2+
netcli
3+
netcli_test

Makefile

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
PROJNAME=netcli
2+
TESTNAME=$(PROJNAME)_test
3+
4+
CC=clang
5+
CXX=clang++
6+
7+
CFLAGS=-fPIC -g -Wall -Wextra -Werror -Winline -MD
8+
9+
LIBS=-lpthread
10+
TEST_LIBS=-lgtest -lgtest_main
11+
12+
C=$(shell find ./src -name "*.c")
13+
TEST_CPP=$(shell find ./test -name "*.cpp" 2> /dev/null)
14+
15+
OBJ=$(patsubst %,obj/%.o,$(basename $(C)))
16+
TEST_OBJ=$(patsubst %,obj/%.o,$(basename $(TEST_CPP)))
17+
18+
default: all
19+
20+
obj/./src/%.o: src/%.c
21+
@mkdir -p $(@D)
22+
@echo -e "\033[0;32mCompiling $<"
23+
@$(CC) $(CFLAGS) -c $< -o $@
24+
25+
obj/./test/%.o: test/%.cpp
26+
@mkdir -p $(@D)
27+
@echo -e "\033[0;32mCompiling $<"
28+
@$(CXX) $(CFLAGS) -I . -c $< -o $@
29+
30+
$(PROJNAME): $(OBJ)
31+
@echo -e "\033[0;36mLinking $@"
32+
@$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
33+
@echo -e "\033[0;35mSrc done"
34+
35+
$(TESTNAME): $(TEST_OBJ) $(filter-out obj/./src/main.o, $(OBJ))
36+
@echo -e "\033[0;36mLinking $@"
37+
@$(CXX) $(CFLAGS) -o $(TESTNAME) $^ $(LIBS) $(TEST_LIBS)
38+
@echo -e "\033[0;35mTest done"
39+
40+
all: $(PROJNAME) $(PROJNAME)_test
41+
@echo -e "\033[0;37mAll done"
42+
43+
clean:
44+
@echo -e "\033[1;33mCleaning up"
45+
@rm $(PROJNAME) -f
46+
@rm $(TESTNAME) -f
47+
@rm $(OBJ) -f
48+
@rm $(patsubst %.o, %.d, $(OBJ))
49+
@rm $(TEST_OBJ) -f
50+
@rm $(patsubst %.o, %.d, $(TEST_OBJ))
51+
52+
-include $(OBJ:.o=.d)
53+
-include $(TEST_OBJ:.o=.d)

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
# net-cli
2+
3+
project for learning to interract with the kernel through the netlink interface

src/commands.c

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
#include "commands.h"
2+
#include <linux/netlink.h>
3+
#include <linux/rtnetlink.h>
4+
#include <arpa/inet.h>
5+
#include <unistd.h>
6+
#include <stdlib.h>
7+
8+
9+
static int netlink_route(struct sockaddr_nl* out_sa) {
10+
// create the netlink socket with the NETLINK_ROUTE protocol
11+
int fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
12+
if (fd < 0)
13+
return -1;
14+
15+
// create the netlink sockaddr
16+
*out_sa = (struct sockaddr_nl) {
17+
.nl_family = AF_NETLINK
18+
};
19+
20+
// bind the file descriptor to the address
21+
int res = bind(fd, (const struct sockaddr*)out_sa, sizeof(*out_sa));
22+
if (res != 0) {
23+
close(fd);
24+
return -1;
25+
}
26+
27+
return fd;
28+
}
29+
30+
31+
int get_local_ips(int family, ip_callback_t callback, void* args) {
32+
struct sockaddr_nl sa;
33+
int fd = netlink_route(&sa);
34+
if (fd == -1)
35+
return -1;
36+
37+
// calculate the total buffer length
38+
const size_t buffer_length = NLMSG_ALIGN(NLMSG_LENGTH(sizeof(struct ifaddrmsg)));
39+
unsigned char buffer[buffer_length];
40+
41+
// create the header of the message
42+
struct nlmsghdr* nlh = (void*)buffer;
43+
*nlh = (struct nlmsghdr){
44+
.nlmsg_len = buffer_length,
45+
.nlmsg_type = RTM_GETADDR,
46+
.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT
47+
};
48+
49+
// create the payload of the message
50+
struct ifaddrmsg* ifa = NLMSG_DATA(nlh);
51+
*ifa = (struct ifaddrmsg){
52+
.ifa_family = family
53+
};
54+
55+
// create the iovec structure containing the buffer and length
56+
struct iovec iov = {
57+
.iov_base = buffer,
58+
.iov_len = buffer_length
59+
};
60+
61+
// create the message header structure
62+
struct msghdr msg = {
63+
.msg_name = &sa,
64+
.msg_namelen = sizeof(sa),
65+
.msg_iov = &iov,
66+
.msg_iovlen = 1
67+
};
68+
69+
// send the message to kernel
70+
ssize_t sent = sendmsg(fd, &msg, 0);
71+
if (sent < 0)
72+
goto close_fd;
73+
74+
iov.iov_len = 0;
75+
76+
// peek the data to see how many bytes are there to read
77+
ssize_t received = recvmsg(fd, &msg, MSG_PEEK | MSG_TRUNC);
78+
if (received < 0)
79+
goto close_fd;
80+
81+
size_t len = (size_t)received;
82+
83+
// allocate the needed memory
84+
unsigned char* recv_buffer = malloc(len);
85+
if (!recv_buffer)
86+
goto close_fd;
87+
88+
iov.iov_base = (void*)recv_buffer;
89+
iov.iov_len = len;
90+
91+
// receive the actual messages
92+
received = recvmsg(fd, &msg, 0);
93+
if (received < 0)
94+
goto free_buf;
95+
96+
len = (size_t)received;
97+
98+
// iterate from every message available
99+
for (nlh = (void*)recv_buffer; NLMSG_OK(nlh, len); nlh = NLMSG_NEXT(nlh, len)) {
100+
struct ifaddrmsg* ifa = (void*)NLMSG_DATA(nlh);
101+
102+
size_t rta_len = IFA_PAYLOAD(nlh);
103+
104+
// iterate from every attribute of the message
105+
for (struct rtattr* rta = (void*)IFA_RTA(ifa); RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
106+
// if the attribute is an address, create the structure
107+
// and call the callback provided by the user
108+
if (rta->rta_type == IFA_ADDRESS) {
109+
struct ip_entry entry = {
110+
.addr = *((struct sockaddr*)RTA_DATA(rta)),
111+
.index = ifa->ifa_index,
112+
.family = family
113+
};
114+
115+
callback(&entry, args);
116+
}
117+
}
118+
}
119+
120+
free(recv_buffer);
121+
122+
close(fd);
123+
124+
return 0;
125+
126+
free_buf:
127+
free(recv_buffer);
128+
129+
close_fd:
130+
close(fd);
131+
132+
return -1;
133+
}

src/commands.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#ifndef COMMANDS_H
2+
#define COMMANDS_H
3+
4+
#include <sys/socket.h>
5+
#include <stdint.h>
6+
7+
struct ip_entry {
8+
struct sockaddr addr;
9+
uint16_t index;
10+
int family;
11+
};
12+
13+
typedef int (*ip_callback_t)(struct ip_entry* adr, void* args);
14+
15+
// all the local ips for every interface
16+
int get_local_ips(int family, ip_callback_t callback, void* args);
17+
18+
#endif

src/main.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include <sys/socket.h>
2+
#include <stdlib.h>
3+
#include <arpa/inet.h>
4+
#include <net/if.h>
5+
#include <stdio.h>
6+
7+
#include "commands.h"
8+
9+
int ip_callback(struct ip_entry* addr, void* args) {
10+
(void)args;
11+
12+
char ip[INET_ADDRSTRLEN];
13+
inet_ntop(AF_INET, &addr->addr, ip, sizeof(ip));
14+
15+
char interface[IF_NAMESIZE];
16+
printf("%s -> ip: %s\n", if_indextoname(addr->index, interface), ip);
17+
18+
return 0;
19+
}
20+
21+
int main(int argc, char **argv) {
22+
(void)argv;
23+
(void)argc;
24+
25+
if (get_local_ips(AF_INET, ip_callback, NULL) != 0) {
26+
return -1;
27+
}
28+
29+
return 0;
30+
}

0 commit comments

Comments
 (0)