blob: 9c52a27a7dc8c1e07598fece832c02c6fcb93c8f [file] [log] [blame]
Harald Welte51b00a62014-04-03 09:37:38 -04001/* Command line utility to create GTP tunnels (PDP contexts) */
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
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
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
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * 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 <string.h>
26#include <unistd.h>
27#include <time.h>
28#include <arpa/inet.h>
29#include <sys/socket.h>
30#include <netinet/in.h>
31
32#include <libmnl/libmnl.h>
33#include <linux/genetlink.h>
34
35#include <linux/gtp_nl.h>
36#include <libgtpnl/gtpnl.h>
37
38static int
39add_tunnel(int argc, char *argv[], int genl_id, struct mnl_socket *nl)
40{
41 uint32_t gtp_ifidx;
42 struct in_addr ms, sgsn;
43 struct nlmsghdr *nlh;
44 char buf[MNL_SOCKET_BUFFER_SIZE];
45 uint32_t seq = time(NULL), gtp_version;
46
47 if (argc != 7) {
48 printf("%s add <gtp device> <v0|v1> <tid> <ms-addr> <sgsn-addr>\n",
49 argv[0]);
50 return EXIT_FAILURE;
51 }
52 gtp_ifidx = if_nametoindex(argv[2]);
53 if (gtp_ifidx == 0) {
54 fprintf(stderr, "wrong GTP interface %s\n", argv[2]);
55 return EXIT_FAILURE;
56 }
57
58 if (inet_aton(argv[5], &ms) < 0) {
59 perror("bad address for ms");
60 exit(EXIT_FAILURE);
61 }
62
63 if (inet_aton(argv[6], &sgsn) < 0) {
64 perror("bad address for sgsn");
65 exit(EXIT_FAILURE);
66 }
67
68 if (strcmp(argv[3], "v0") == 0)
69 gtp_version = GTP_V0;
70 else if (strcmp(argv[3], "v1") == 0)
71 gtp_version = GTP_V1;
72 else {
73 fprintf(stderr, "wrong GTP version %s, use v0 or v1\n",
74 argv[3]);
75 return EXIT_FAILURE;
76 }
77
78 nlh = genl_nlmsg_build_hdr(buf, genl_id, NLM_F_EXCL | NLM_F_ACK, ++seq,
79 GTP_CMD_TUNNEL_NEW);
80 gtp_build_payload(nlh, atoi(argv[4]), gtp_ifidx, sgsn.s_addr,
81 ms.s_addr, gtp_version);
82
83 if (genl_socket_talk(nl, nlh, seq, NULL, NULL) < 0)
84 perror("genl_socket_talk");
85
86 return 0;
87}
88
89static int
90del_tunnel(int argc, char *argv[], int genl_id, struct mnl_socket *nl)
91{
92 uint32_t gtp_ifidx;
93 char buf[MNL_SOCKET_BUFFER_SIZE];
94 struct nlmsghdr *nlh;
95 uint32_t seq = time(NULL);
96
97 if (argc != 5) {
98 printf("%s add <gtp device> <version> <tid>\n",
99 argv[0]);
100 return EXIT_FAILURE;
101 }
102
103 gtp_ifidx = if_nametoindex(argv[2]);
104
105 nlh = genl_nlmsg_build_hdr(buf, genl_id, NLM_F_ACK, ++seq,
106 GTP_CMD_TUNNEL_DELETE);
107 gtp_build_payload(nlh, atoi(argv[4]), gtp_ifidx, 0, 0, atoi(argv[3]));
108
109 if (genl_socket_talk(nl, nlh, seq, NULL, NULL) < 0)
110 perror("genl_socket_talk");
111
112 return 0;
113}
114
115struct gtp_pdp {
116 uint32_t version;
117 uint64_t tid;
118 struct in_addr sgsn_addr;
119 struct in_addr ms_addr;
120};
121
122static int genl_gtp_validate_cb(const struct nlattr *attr, void *data)
123{
124 const struct nlattr **tb = data;
125 int type = mnl_attr_get_type(attr);
126
127 if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0)
128 return MNL_CB_OK;
129
130 switch(type) {
131 case GTPA_TID:
132 if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0) {
133 perror("mnl_attr_validate");
134 return MNL_CB_ERROR;
135 }
136 break;
137 case GTPA_SGSN_ADDRESS:
138 case GTPA_MS_ADDRESS:
139 case GTPA_VERSION:
140 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
141 perror("mnl_attr_validate");
142 return MNL_CB_ERROR;
143 }
144 break;
145 default:
146 break;
147 }
148 tb[type] = attr;
149 return MNL_CB_OK;
150}
151
152static int genl_gtp_attr_cb(const struct nlmsghdr *nlh, void *data)
153{
154 struct nlattr *tb[GTPA_MAX + 1] = {};
155 struct gtp_pdp pdp;
156 struct genlmsghdr *genl;
157
158 mnl_attr_parse(nlh, sizeof(*genl), genl_gtp_validate_cb, tb);
159 if (tb[GTPA_TID])
160 pdp.tid = mnl_attr_get_u64(tb[GTPA_TID]);
161 if (tb[GTPA_SGSN_ADDRESS]) {
162 pdp.sgsn_addr.s_addr =
163 mnl_attr_get_u32(tb[GTPA_SGSN_ADDRESS]);
164 }
165 if (tb[GTPA_MS_ADDRESS]) {
166 pdp.ms_addr.s_addr = mnl_attr_get_u32(tb[GTPA_MS_ADDRESS]);
167 }
168 if (tb[GTPA_VERSION]) {
169 pdp.version = mnl_attr_get_u32(tb[GTPA_VERSION]);
170 }
171
172 printf("version %u ", pdp.version);
Pablo Neira Ayusob0d712b2014-03-20 12:56:31 +0100173 printf("tid %llx ms_addr %s ", pdp.tid, inet_ntoa(pdp.ms_addr));
Pablo Neira Ayusof8ca7652014-02-22 22:38:05 +0100174 printf("sgsn_addr %s\n", inet_ntoa(pdp.sgsn_addr));
Pablo Neira Ayuso14506662014-02-20 18:43:15 +0100175
176 return MNL_CB_OK;
177}
178
179static int
180list_tunnel(int argc, char *argv[], int genl_id, struct mnl_socket *nl)
181{
182 char buf[MNL_SOCKET_BUFFER_SIZE];
183 struct nlmsghdr *nlh;
184 uint32_t seq = time(NULL);
185
186 nlh = genl_nlmsg_build_hdr(buf, genl_id, NLM_F_DUMP, 0,
187 GTP_CMD_TUNNEL_GET);
188
189 if (genl_socket_talk(nl, nlh, seq, genl_gtp_attr_cb, NULL) < 0) {
190 perror("genl_socket_talk");
191 return 0;
192 }
193
194 return 0;
195}
196
197int main(int argc, char *argv[])
198{
199 struct mnl_socket *nl;
200 char buf[MNL_SOCKET_BUFFER_SIZE];
201 unsigned int portid;
202 int32_t genl_id;
203 int ret;
204
205 if (argc < 2) {
206 printf("%s <add|delete|list> [<options,...>]\n", argv[0]);
207 exit(EXIT_FAILURE);
208 }
209
210 nl = genl_socket_open();
211 if (nl == NULL) {
212 perror("mnl_socket_open");
213 exit(EXIT_FAILURE);
214 }
215
216 genl_id = genl_lookup_family(nl, "gtp");
217 if (genl_id < 0) {
218 printf("not found gtp genl family\n");
219 exit(EXIT_FAILURE);
220 }
221
222 if (strncmp(argv[1], "add", strlen(argv[1])) == 0)
223 ret = add_tunnel(argc, argv, genl_id, nl);
224 else if (strncmp(argv[1], "delete", strlen(argv[1])) == 0)
225 ret = del_tunnel(argc, argv, genl_id, nl);
226 else if (strncmp(argv[1], "list", strlen(argv[1])) == 0)
227 ret = list_tunnel(argc, argv, genl_id, nl);
228 else {
229 printf("Unknown command `%s'\n", argv[1]);
230 exit(EXIT_FAILURE);
231 }
232
233 mnl_socket_close(nl);
234
235 return ret;
236}