aboutsummaryrefslogtreecommitdiff
path: root/src/getdefgw.c
blob: 8f9ad65e08f099726fb95b0226a9b1e6af4fe595 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/* This file is part of tallyman
Copyright (C) 2018 Sergey Poznyakoff
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
*/
#include <stdlib.h>
#include <string.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <net/if.h>
#include <limits.h>
#include <errno.h>

#define BUFFER_SIZE 4096

int
getdefgw(struct in_addr *ret)
{
	int fd;
	static int msgseq = 0;
	struct  nlmsghdr *nlmsg, *hdr;
	char    msgbuf[BUFFER_SIZE];
	char buffer[BUFFER_SIZE];
	size_t rdlen = 0;
	ssize_t nbytes;
	struct timeval tv;
	size_t last_metrics = INT_MAX;
	int ec;
	int retval;
	
	if (!ret) {
		errno = EINVAL;
		return -1;
	}
	
	if ((fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0)
		return -1;

	memset(msgbuf, 0, sizeof(msgbuf));
	memset(buffer, 0, sizeof(buffer));

	nlmsg = (struct nlmsghdr *)msgbuf;
	nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg));
	nlmsg->nlmsg_type = RTM_GETROUTE;
	nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST;
	nlmsg->nlmsg_seq = msgseq++;
	nlmsg->nlmsg_pid = getpid();

	tv.tv_sec = 1;
	setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

	if (send(fd, nlmsg, nlmsg->nlmsg_len, 0) < 0) {
		retval = -1;
		goto err;
	}

	retval = 1;
	do {
		nbytes = recv(fd, buffer, sizeof(buffer), 0);
		if (nbytes < 0) {
			if (errno == EINTR)
				continue;
			retval = -1;
			goto err;
		}

		hdr = (struct nlmsghdr *) buffer;
	} while ((hdr->nlmsg_seq != nlmsg->nlmsg_seq)
		 || (hdr->nlmsg_pid != getpid()));
	
	do {
		hdr = (struct nlmsghdr *) (buffer + rdlen);

		if (hdr->nlmsg_seq != nlmsg->nlmsg_seq
		    || hdr->nlmsg_pid != getpid())
			break;
				
		if (hdr->nlmsg_type == NLMSG_ERROR
		    || NLMSG_OK(hdr, nbytes) == 0) {
			retval = -1;
			goto err;
		}

		if (hdr->nlmsg_type == NLMSG_DONE)
			break;
		else
			rdlen += nbytes;

		if ((hdr->nlmsg_flags & NLM_F_MULTI) == 0)
			break;

		nbytes = recv(fd, buffer + rdlen, sizeof(buffer) - rdlen, 0);
		if (nbytes < 0) {
			retval = -1;
			goto err;
		}
	} while (nbytes > 0);

	for (hdr = (struct nlmsghdr *)buffer;
	     NLMSG_OK(hdr, rdlen);
	     hdr = NLMSG_NEXT(hdr, rdlen)) {
		struct  rtmsg *rtm;
		struct  rtattr *rta;
		size_t rta_len;
		struct in_addr dst = { 0 };
		struct in_addr gw = { 0 };
		int metrics = 0;
	
		rtm = (struct rtmsg *) NLMSG_DATA(hdr);

		if (rtm->rtm_table != RT_TABLE_MAIN)
			continue;

		rta = (struct rtattr *) RTM_RTA(rtm);
		rta_len = RTM_PAYLOAD(hdr);

		for (; RTA_OK(rta, rta_len); rta = RTA_NEXT(rta, rta_len)) {
			switch (rta->rta_type) {
			case RTA_DST:
				dst = *(struct in_addr *) RTA_DATA(rta);
				break;
			case RTA_METRICS:
				metrics = *(int *) RTA_DATA(rta);
				break;
			case RTA_GATEWAY:
				gw = *(struct in_addr *)RTA_DATA(rta);
				break;
			default:
				break;
			}
		}
				
		if (gw.s_addr != 0
		    && dst.s_addr == 0 && metrics < last_metrics) {
			retval = 0;
			*ret = gw;
			last_metrics = metrics;
		}
	}

err:
	ec = errno;
	close(fd);
	errno = ec;
	return retval;
}

Return to:

Send suggestions and report system problems to the System administrator.