blob: 6bfec653a92d2f6a14efd4b96e896d3ec3b6c3e9 [file] [log] [blame]
Harald Welte51b00a62014-04-03 09:37:38 -04001/* GTP specific RTNetlink helper functions */
2
3/* (C) 2014 by sysmocom - s.f.m.c. GmbH
4 * Author: Pablo Neira Ayuso <pablo@gnumonks.org>
5 *
6 * All Rights Reserved
7 *
8 * This program is free software; you can redistribute it and/or modify
Harald Welted2bb0bc2016-07-28 20:34:45 +02009 * it under the terms of the GNU Lesser General Public License as
10 * published by the Free Software Foundation; either version 2 of the
11 * License, or (at your option) any later version.
Harald Welte51b00a62014-04-03 09:37:38 -040012 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Welted2bb0bc2016-07-28 20:34:45 +020016 * GNU Lesser General Public License for more details.
Harald Welte51b00a62014-04-03 09:37:38 -040017 *
Harald Welted2bb0bc2016-07-28 20:34:45 +020018 * You should have received a copy of the GNU Lesser General Public License
Harald Welte51b00a62014-04-03 09:37:38 -040019 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010023#include <stdio.h>
24#include <stdlib.h>
25#include <unistd.h>
26#include <string.h>
27#include <time.h>
28
29#include <libmnl/libmnl.h>
30#include <net/if.h>
31#include <linux/if_link.h>
32#include <linux/rtnetlink.h>
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010033#include <libgtpnl/gtpnl.h>
Pablo Neira Ayusob9f6ffe2016-05-08 18:27:55 +020034#include <linux/gtp.h>
35#include <linux/if_link.h>
Pablo Neira Ayusodeb54082014-03-20 16:23:18 +010036#include <netinet/in.h>
37#include <arpa/inet.h>
38#include <time.h>
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010039
40#include "internal.h"
41
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +010042static struct nlmsghdr *
43gtp_put_nlmsg(char *buf, uint16_t type, uint16_t nl_flags, uint32_t seq)
44{
45 struct nlmsghdr *nlh;
46
47 nlh = mnl_nlmsg_put_header(buf);
48 nlh->nlmsg_type = type;
49 nlh->nlmsg_flags = NLM_F_REQUEST | nl_flags;
50 nlh->nlmsg_seq = seq;
51
52 return nlh;
53}
54
Pablo Neira Ayuso1f683292014-03-20 16:21:29 +010055static struct mnl_socket *rtnl_open(void)
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010056{
57 struct mnl_socket *nl;
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010058
59 nl = mnl_socket_open(NETLINK_ROUTE);
60 if (nl == NULL) {
61 perror("mnl_socket_open");
Pablo Neira Ayuso1f683292014-03-20 16:21:29 +010062 return NULL;
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010063 }
64
65 if (mnl_socket_bind(nl, 0, MNL_SOCKET_AUTOPID) < 0) {
66 perror("mnl_socket_bind");
Pablo Neira Ayuso3876ef62014-02-22 22:50:00 +010067 goto err;
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010068 }
Pablo Neira Ayuso14506662014-02-20 18:43:15 +010069
Pablo Neira Ayuso1f683292014-03-20 16:21:29 +010070 return nl;
Pablo Neira Ayuso3876ef62014-02-22 22:50:00 +010071err:
72 mnl_socket_close(nl);
Pablo Neira Ayuso1f683292014-03-20 16:21:29 +010073 return NULL;
74}
75
76static int rtnl_talk(struct mnl_socket *nl, struct nlmsghdr *nlh)
77{
78 char buf[MNL_SOCKET_BUFFER_SIZE];
79 int ret;
80
81 ret = mnl_socket_sendto(nl, nlh, nlh->nlmsg_len);
82 if (ret < 0)
83 return ret;
84
85 ret = mnl_socket_recvfrom(nl, buf, sizeof(buf));
86 if (ret < 0)
87 return ret;
88
89 return mnl_cb_run(buf, ret, nlh->nlmsg_seq, mnl_socket_get_portid(nl),
90 NULL, NULL);
91}
92
93static int gtp_dev_talk(struct nlmsghdr *nlh, uint32_t seq)
94{
95 struct mnl_socket *nl;
96 int ret;
97
98 nl = rtnl_open();
99 if (nl == NULL)
100 return -1;
101
102 ret = rtnl_talk(nl, nlh);
103
104 mnl_socket_close(nl);
105 return ret;
Pablo Neira Ayuso14506662014-02-20 18:43:15 +0100106}
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +0100107
Andreas Schultzaf422a42016-04-11 16:10:02 +0200108int gtp_dev_create(int dest_ns, const char *gtp_ifname, int fd0, int fd1)
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +0100109{
110 char buf[MNL_SOCKET_BUFFER_SIZE];
111 struct nlmsghdr *nlh;
112 struct ifinfomsg *ifm;
113 unsigned int seq = time(NULL);
114 struct nlattr *nest, *nest2;
115
Pablo Neira Ayuso7aa20872014-02-24 11:38:52 +0100116 nlh = gtp_put_nlmsg(buf, RTM_NEWLINK,
117 NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK, seq);
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +0100118 ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
119 ifm->ifi_family = AF_INET;
120 ifm->ifi_change |= IFF_UP;
121 ifm->ifi_flags |= IFF_UP;
122
Andreas Schultz49773302016-04-11 16:09:56 +0200123 if (dest_ns >= 0)
124 mnl_attr_put_u32(nlh, IFLA_NET_NS_FD, dest_ns);
Pablo Neira Ayuso7aa20872014-02-24 11:38:52 +0100125 mnl_attr_put_str(nlh, IFLA_IFNAME, gtp_ifname);
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +0100126 nest = mnl_attr_nest_start(nlh, IFLA_LINKINFO);
127 mnl_attr_put_str(nlh, IFLA_INFO_KIND, "gtp");
128 nest2 = mnl_attr_nest_start(nlh, IFLA_INFO_DATA);
Pablo Neira Ayusodea76a02014-02-23 21:55:42 +0100129 mnl_attr_put_u32(nlh, IFLA_GTP_FD0, fd0);
130 mnl_attr_put_u32(nlh, IFLA_GTP_FD1, fd1);
Pablo Neira Ayusob9f6ffe2016-05-08 18:27:55 +0200131 mnl_attr_put_u32(nlh, IFLA_GTP_PDP_HASHSIZE, 131072);
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +0100132 mnl_attr_nest_end(nlh, nest2);
133 mnl_attr_nest_end(nlh, nest);
134
135 return gtp_dev_talk(nlh, seq);
136}
Pablo Neira Ayuso14506662014-02-20 18:43:15 +0100137EXPORT_SYMBOL(gtp_dev_create);
Pablo Neira Ayuso41ff5382014-02-22 23:07:41 +0100138
Pablo Neira Ayuso7aa20872014-02-24 11:38:52 +0100139int gtp_dev_destroy(const char *gtp_ifname)
Pablo Neira Ayuso41ff5382014-02-22 23:07:41 +0100140{
141 char buf[MNL_SOCKET_BUFFER_SIZE];
142 struct nlmsghdr *nlh;
143 struct ifinfomsg *ifm;
144 unsigned int seq = time(NULL);
145
146 nlh = gtp_put_nlmsg(buf, RTM_DELLINK, NLM_F_ACK, seq);
147 ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
148 ifm->ifi_family = AF_INET;
149 ifm->ifi_change |= IFF_UP;
150 ifm->ifi_flags &= ~IFF_UP;
Pablo Neira Ayuso7aa20872014-02-24 11:38:52 +0100151 ifm->ifi_index = if_nametoindex(gtp_ifname);
Pablo Neira Ayuso41ff5382014-02-22 23:07:41 +0100152
153 return gtp_dev_talk(nlh, seq);
154}
155EXPORT_SYMBOL(gtp_dev_destroy);
Pablo Neira Ayusodeb54082014-03-20 16:23:18 +0100156
157int gtp_dev_config(const char *ifname, struct in_addr *dst, uint32_t prefix)
158{
159 struct mnl_socket *nl;
160 char buf[MNL_SOCKET_BUFFER_SIZE];
161 struct nlmsghdr *nlh;
162 struct rtmsg *rtm;
163 int iface, ret;
164
165 iface = if_nametoindex(ifname);
166 if (iface == 0) {
167 perror("if_nametoindex");
168 return -1;
169 }
170
171 nlh = mnl_nlmsg_put_header(buf);
172 nlh->nlmsg_type = RTM_NEWROUTE;
173 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
174 nlh->nlmsg_seq = time(NULL);
175
176 rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg));
177 rtm->rtm_family = AF_INET;
178 rtm->rtm_dst_len = prefix;
179 rtm->rtm_src_len = 0;
180 rtm->rtm_tos = 0;
181 rtm->rtm_protocol = RTPROT_STATIC;
182 rtm->rtm_table = RT_TABLE_MAIN;
183 rtm->rtm_type = RTN_UNICAST;
184 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
185 rtm->rtm_flags = 0;
186
187 mnl_attr_put_u32(nlh, RTA_DST, dst->s_addr);
188 mnl_attr_put_u32(nlh, RTA_OIF, iface);
189
190 nl = rtnl_open();
191 if (nl == NULL)
192 return -1;
193
194 ret = rtnl_talk(nl, nlh);
195
196 mnl_socket_close(nl);
197
198 return ret;
199}
200EXPORT_SYMBOL(gtp_dev_config);