blob: 1dfd12544268fae35a58f41b49e9ac5cccd7118c [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
Harald Welte6d60a402017-03-15 18:03:42 +0100108static int _gtp_dev_create(int dest_ns, const char *gtp_ifname, int fd0,
109 int fd1, enum ifla_gtp_role role)
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +0100110{
111 char buf[MNL_SOCKET_BUFFER_SIZE];
112 struct nlmsghdr *nlh;
113 struct ifinfomsg *ifm;
114 unsigned int seq = time(NULL);
115 struct nlattr *nest, *nest2;
116
Pablo Neira Ayuso7aa20872014-02-24 11:38:52 +0100117 nlh = gtp_put_nlmsg(buf, RTM_NEWLINK,
118 NLM_F_CREATE | NLM_F_EXCL | NLM_F_ACK, seq);
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +0100119 ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
120 ifm->ifi_family = AF_INET;
121 ifm->ifi_change |= IFF_UP;
122 ifm->ifi_flags |= IFF_UP;
123
Andreas Schultz49773302016-04-11 16:09:56 +0200124 if (dest_ns >= 0)
125 mnl_attr_put_u32(nlh, IFLA_NET_NS_FD, dest_ns);
Pablo Neira Ayuso7aa20872014-02-24 11:38:52 +0100126 mnl_attr_put_str(nlh, IFLA_IFNAME, gtp_ifname);
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +0100127 nest = mnl_attr_nest_start(nlh, IFLA_LINKINFO);
128 mnl_attr_put_str(nlh, IFLA_INFO_KIND, "gtp");
129 nest2 = mnl_attr_nest_start(nlh, IFLA_INFO_DATA);
Pablo Neira Ayusodea76a02014-02-23 21:55:42 +0100130 mnl_attr_put_u32(nlh, IFLA_GTP_FD0, fd0);
131 mnl_attr_put_u32(nlh, IFLA_GTP_FD1, fd1);
Pablo Neira Ayusob9f6ffe2016-05-08 18:27:55 +0200132 mnl_attr_put_u32(nlh, IFLA_GTP_PDP_HASHSIZE, 131072);
Harald Welte3bf55c32017-03-15 18:03:42 +0100133 if (role != GTP_ROLE_GGSN)
134 mnl_attr_put_u32(nlh, IFLA_GTP_ROLE, role);
Pablo Neira Ayuso10df0592014-02-22 22:57:07 +0100135 mnl_attr_nest_end(nlh, nest2);
136 mnl_attr_nest_end(nlh, nest);
137
138 return gtp_dev_talk(nlh, seq);
139}
Harald Welte6d60a402017-03-15 18:03:42 +0100140
141int gtp_dev_create(int dest_ns, const char *gtp_ifname, int fd0, int fd1)
142{
143 return _gtp_dev_create(dest_ns, gtp_ifname, fd0, fd1, GTP_ROLE_GGSN);
144}
Pablo Neira Ayuso14506662014-02-20 18:43:15 +0100145EXPORT_SYMBOL(gtp_dev_create);
Pablo Neira Ayuso41ff5382014-02-22 23:07:41 +0100146
Harald Welte6d60a402017-03-15 18:03:42 +0100147int gtp_dev_create_sgsn(int dest_ns, const char *gtp_ifname, int fd0, int fd1)
148{
149 return _gtp_dev_create(dest_ns, gtp_ifname, fd0, fd1, GTP_ROLE_SGSN);
150}
151EXPORT_SYMBOL(gtp_dev_create_sgsn);
152
Pablo Neira Ayuso7aa20872014-02-24 11:38:52 +0100153int gtp_dev_destroy(const char *gtp_ifname)
Pablo Neira Ayuso41ff5382014-02-22 23:07:41 +0100154{
155 char buf[MNL_SOCKET_BUFFER_SIZE];
156 struct nlmsghdr *nlh;
157 struct ifinfomsg *ifm;
158 unsigned int seq = time(NULL);
159
160 nlh = gtp_put_nlmsg(buf, RTM_DELLINK, NLM_F_ACK, seq);
161 ifm = mnl_nlmsg_put_extra_header(nlh, sizeof(*ifm));
162 ifm->ifi_family = AF_INET;
163 ifm->ifi_change |= IFF_UP;
164 ifm->ifi_flags &= ~IFF_UP;
Pablo Neira Ayuso7aa20872014-02-24 11:38:52 +0100165 ifm->ifi_index = if_nametoindex(gtp_ifname);
Pablo Neira Ayuso41ff5382014-02-22 23:07:41 +0100166
167 return gtp_dev_talk(nlh, seq);
168}
169EXPORT_SYMBOL(gtp_dev_destroy);
Pablo Neira Ayusodeb54082014-03-20 16:23:18 +0100170
171int gtp_dev_config(const char *ifname, struct in_addr *dst, uint32_t prefix)
172{
173 struct mnl_socket *nl;
174 char buf[MNL_SOCKET_BUFFER_SIZE];
175 struct nlmsghdr *nlh;
176 struct rtmsg *rtm;
177 int iface, ret;
178
179 iface = if_nametoindex(ifname);
180 if (iface == 0) {
181 perror("if_nametoindex");
182 return -1;
183 }
184
185 nlh = mnl_nlmsg_put_header(buf);
186 nlh->nlmsg_type = RTM_NEWROUTE;
187 nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_ACK;
188 nlh->nlmsg_seq = time(NULL);
189
190 rtm = mnl_nlmsg_put_extra_header(nlh, sizeof(struct rtmsg));
191 rtm->rtm_family = AF_INET;
192 rtm->rtm_dst_len = prefix;
193 rtm->rtm_src_len = 0;
194 rtm->rtm_tos = 0;
195 rtm->rtm_protocol = RTPROT_STATIC;
196 rtm->rtm_table = RT_TABLE_MAIN;
197 rtm->rtm_type = RTN_UNICAST;
198 rtm->rtm_scope = RT_SCOPE_UNIVERSE;
199 rtm->rtm_flags = 0;
200
201 mnl_attr_put_u32(nlh, RTA_DST, dst->s_addr);
202 mnl_attr_put_u32(nlh, RTA_OIF, iface);
203
204 nl = rtnl_open();
205 if (nl == NULL)
206 return -1;
207
208 ret = rtnl_talk(nl, nlh);
209
210 mnl_socket_close(nl);
211
212 return ret;
213}
214EXPORT_SYMBOL(gtp_dev_config);