blob: 6ecea086413ff26769b4a5798bd9241a86782cdf [file] [log] [blame]
Pablo Neira Ayuso14506662014-02-20 18:43:15 +01001#include <stdio.h>
2#include <stdlib.h>
3#include <unistd.h>
4#include <string.h>
5#include <time.h>
6
7#include <libmnl/libmnl.h>
8#include <net/if.h>
9#include <linux/if_link.h>
10#include <linux/rtnetlink.h>
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010011#include <libgtpnl/gtpnl.h>
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010012#include <linux/gtp_nl.h>
Pablo Neira Ayusodeb54082014-03-20 16:23:18 +010013#include <netinet/in.h>
14#include <arpa/inet.h>
15#include <time.h>
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010016
17#include "internal.h"
18
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +010019static struct nlmsghdr *
20gtp_put_nlmsg(char *buf, uint16_t type, uint16_t nl_flags, uint32_t seq)
21{
22 struct nlmsghdr *nlh;
23
24 nlh = mnl_nlmsg_put_header(buf);
25 nlh->nlmsg_type = type;
26 nlh->nlmsg_flags = NLM_F_REQUEST | nl_flags;
27 nlh->nlmsg_seq = seq;
28
29 return nlh;
30}
31
Pablo Neira Ayuso1f683292014-03-20 16:21:29 +010032static struct mnl_socket *rtnl_open(void)
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010033{
34 struct mnl_socket *nl;
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010035
36 nl = mnl_socket_open(NETLINK_ROUTE);
37 if (nl == NULL) {
38 perror("mnl_socket_open");
Pablo Neira Ayuso1f683292014-03-20 16:21:29 +010039 return NULL;
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010040 }
41
42 if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
43 perror("mnl_socket_bind");
Pablo Neira Ayuso3876ef62014-02-22 22:50:00 +010044 goto err;
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010045 }
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010046
Pablo Neira Ayuso1f683292014-03-20 16:21:29 +010047 return nl;
Pablo Neira Ayuso3876ef62014-02-22 22:50:00 +010048err:
49 mnl_socket_close(nl);
Pablo Neira Ayuso1f683292014-03-20 16:21:29 +010050 return NULL;
51}
52
53static int rtnl_talk(struct mnl_socket *nl, struct nlmsghdr *nlh)
54{
55 char buf[MNL_SOCKET_BUFFER_SIZE];
56 int ret;
57
58 ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len);
59 if (ret < 0)
60 return ret;
61
62 ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
63 if (ret < 0)
64 return ret;
65
66 return mnl_cb_run(buf, ret, nlh->nlmsg_seq, mnl_socket_get_portid(nl),
67 NULL, NULL);
68}
69
70static int gtp_dev_talk(struct nlmsghdr *nlh, uint32_t seq)
71{
72 struct mnl_socket *nl;
73 int ret;
74
75 nl = rtnl_open();
76 if (nl == NULL)
77 return -1;
78
79 ret = rtnl_talk(nl, nlh);
80
81 mnl_socket_close(nl);
82 return ret;
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010083}
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +010084
Pablo Neira Ayuso7aa20872014-02-24 11:38:52 +010085int gtp_dev_create(const char *gtp_ifname, const char *real_ifname,
86 int fd0, int fd1)
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +010087{
88 char buf[MNL_SOCKET_BUFFER_SIZE];
89 struct nlmsghdr *nlh;
90 struct ifinfomsg *ifm;
91 unsigned int seq = time(NULL);
92 struct nlattr *nest, *nest2;
93
Pablo Neira Ayuso7aa20872014-02-24 11:38:52 +010094 nlh = gtp_put_nlmsg(buf, RTM_NEWLINK,
95 NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK, seq);
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +010096 ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
97 ifm->ifi_family = AF_INET;
98 ifm->ifi_change |= IFF_UP;
99 ifm->ifi_flags |= IFF_UP;
100
Pablo Neira Ayuso7aa20872014-02-24 11:38:52 +0100101 mnl_attr_put_u32(nlh, IFLA_LINK, if_nametoindex(real_ifname));
102 mnl_attr_put_str(nlh, IFLA_IFNAME, gtp_ifname);
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +0100103 nest = mnl_attr_nest_start(nlh, IFLA_LINKINFO);
104 mnl_attr_put_str(nlh, IFLA_INFO_KIND, "gtp");
105 nest2 = mnl_attr_nest_start(nlh, IFLA_INFO_DATA);
Pablo Neira Ayusodea76a02014-02-23 21:55:42 +0100106 mnl_attr_put_u32(nlh, IFLA_GTP_FD0, fd0);
107 mnl_attr_put_u32(nlh, IFLA_GTP_FD1, fd1);
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +0100108 mnl_attr_put_u32(nlh, IFLA_GTP_HASHSIZE, 131072);
109 mnl_attr_nest_end(nlh, nest2);
110 mnl_attr_nest_end(nlh, nest);
111
112 return gtp_dev_talk(nlh, seq);
113}
Pablo Neira Ayuso14506662014-02-20 18:43:15 +0100114EXPORT_SYMBOL(gtp_dev_create);
Pablo Neira Ayuso41ff5382014-02-22 23:07:41 +0100115
Pablo Neira Ayuso7aa20872014-02-24 11:38:52 +0100116int gtp_dev_destroy(const char *gtp_ifname)
Pablo Neira Ayuso41ff5382014-02-22 23:07:41 +0100117{
118 char buf[MNL_SOCKET_BUFFER_SIZE];
119 struct nlmsghdr *nlh;
120 struct ifinfomsg *ifm;
121 unsigned int seq = time(NULL);
122
123 nlh = gtp_put_nlmsg(buf, RTM_DELLINK, NLM_F_ACK, seq);
124 ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
125 ifm->ifi_family = AF_INET;
126 ifm->ifi_change |= IFF_UP;
127 ifm->ifi_flags &= ~IFF_UP;
Pablo Neira Ayuso7aa20872014-02-24 11:38:52 +0100128 ifm->ifi_index = if_nametoindex(gtp_ifname);
Pablo Neira Ayuso41ff5382014-02-22 23:07:41 +0100129
130 return gtp_dev_talk(nlh, seq);
131}
132EXPORT_SYMBOL(gtp_dev_destroy);
Pablo Neira Ayusodeb54082014-03-20 16:23:18 +0100133
134int gtp_dev_config(const char *ifname, struct in_addr *dst, uint32_t prefix)
135{
136 struct mnl_socket *nl;
137 char buf[MNL_SOCKET_BUFFER_SIZE];
138 struct nlmsghdr *nlh;
139 struct rtmsg *rtm;
140 int iface, ret;
141
142 iface = if_nametoindex(ifname);
143 if (iface == 0) {
144 perror("if_nametoindex");
145 return -1;
146 }
147
148 nlh = mnl_nlmsg_put_header(buf);
149 nlh->nlmsg_type = RTM_NEWROUTE;
150 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
151 nlh->nlmsg_seq = time(NULL);
152
153 rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg));
154 rtm->rtm_family = AF_INET;
155 rtm->rtm_dst_len = prefix;
156 rtm->rtm_src_len = 0;
157 rtm->rtm_tos = 0;
158 rtm->rtm_protocol = RTPROT_STATIC;
159 rtm->rtm_table = RT_TABLE_MAIN;
160 rtm->rtm_type = RTN_UNICAST;
161 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
162 rtm->rtm_flags = 0;
163
164 mnl_attr_put_u32(nlh, RTA_DST, dst->s_addr);
165 mnl_attr_put_u32(nlh, RTA_OIF, iface);
166
167 nl = rtnl_open();
168 if (nl == NULL)
169 return -1;
170
171 ret = rtnl_talk(nl, nlh);
172
173 mnl_socket_close(nl);
174
175 return ret;
176}
177EXPORT_SYMBOL(gtp_dev_config);