blob: dbbd8d298d72c56d555a8bdc0435925fe180cc01 [file] [log] [blame]
Neels Hofmeyr17518fe2017-06-20 04:35:06 +02001/*! \file gprs_ns_frgre.c
2 * NS-over-FR-over-GRE implementation.
3 * GPRS Networks Service (NS) messages on the Gb interface,
4 * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05). */
5/*
Harald Weltee08da972017-11-13 01:00:26 +09006 * (C) 2009-2010,2014,2017 by Harald Welte <laforge@gnumonks.org>
Harald Welteb3ee2652010-05-19 14:38:50 +02007 *
8 * All Rights Reserved
9 *
Harald Weltee08da972017-11-13 01:00:26 +090010 * SPDX-License-Identifier: GPL-2.0+
11 *
Harald Welteb3ee2652010-05-19 14:38:50 +020012 * This program is free software; you can redistribute it and/or modify
Harald Welte7fa89c22014-10-26 20:33:09 +010013 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
Harald Welteb3ee2652010-05-19 14:38:50 +020015 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Welte7fa89c22014-10-26 20:33:09 +010020 * GNU General Public License for more details.
Harald Welteb3ee2652010-05-19 14:38:50 +020021 *
Harald Welte7fa89c22014-10-26 20:33:09 +010022 * You should have received a copy of the GNU General Public License
Harald Weltee4cbb3f2011-01-01 15:25:50 +010023 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Harald Welteb3ee2652010-05-19 14:38:50 +020024 *
25 */
26
27#include <errno.h>
28#include <string.h>
29#include <unistd.h>
30
31#include <sys/socket.h>
32#include <netinet/in.h>
Harald Welte6c4136e2010-05-19 15:53:22 +020033#include <netinet/ip.h>
Harald Welteb3ee2652010-05-19 14:38:50 +020034#include <arpa/inet.h>
35
Pablo Neira Ayusoff663232011-03-22 16:47:59 +010036#include <osmocom/core/select.h>
Harald Weltebfe62e52017-05-15 12:48:30 +020037#include <osmocom/core/byteswap.h>
Pablo Neira Ayusoff663232011-03-22 16:47:59 +010038#include <osmocom/core/msgb.h>
39#include <osmocom/core/talloc.h>
Harald Welte73952e32012-06-16 14:59:56 +080040#include <osmocom/core/socket.h>
41#include <osmocom/gprs/gprs_ns.h>
Harald Welteb3ee2652010-05-19 14:38:50 +020042
Harald Weltecca49632012-06-16 17:45:59 +080043#include "common_vty.h"
Harald Welteb3ee2652010-05-19 14:38:50 +020044
45#define GRE_PTYPE_FR 0x6559
Harald Welte57a9cf22010-05-28 16:06:53 +020046#define GRE_PTYPE_IPv4 0x0800
47#define GRE_PTYPE_KAR 0x0000 /* keepalive response */
Harald Welteb3ee2652010-05-19 14:38:50 +020048
Evgeny Zverev06ddf8b2013-01-12 00:02:40 +040049#ifndef IPPROTO_GRE
50# define IPPROTO_GRE 47
51#endif
52
Harald Welteb3ee2652010-05-19 14:38:50 +020053struct gre_hdr {
54 uint16_t flags;
55 uint16_t ptype;
56} __attribute__ ((packed));
57
Evgeny Zverev06ddf8b2013-01-12 00:02:40 +040058#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__CYGWIN__)
Holger Hans Peter Freyther3c16de22012-07-12 14:13:41 +020059/**
60 * On BSD the IPv4 struct is called struct ip and instead of iXX
61 * the members are called ip_XX. One could change this code to use
62 * struct ip but that would require to define _BSD_SOURCE and that
63 * might have other complications. Instead make sure struct iphdr
64 * is present on FreeBSD. The below is taken from GLIBC.
65 *
66 * The GNU C Library is free software; you can redistribute it and/or
67 * modify it under the terms of the GNU Lesser General Public
68 * License as published by the Free Software Foundation; either
69 * version 2.1 of the License, or (at your option) any later version.
70 */
71struct iphdr
72 {
73#if BYTE_ORDER == LITTLE_ENDIAN
74 unsigned int ihl:4;
75 unsigned int version:4;
76#elif BYTE_ORDER == BIG_ENDIAN
77 unsigned int version:4;
78 unsigned int ihl:4;
79#endif
80 u_int8_t tos;
81 u_int16_t tot_len;
82 u_int16_t id;
83 u_int16_t frag_off;
84 u_int8_t ttl;
85 u_int8_t protocol;
86 u_int16_t check;
87 u_int32_t saddr;
88 u_int32_t daddr;
89 /*The options start here. */
90 };
91#endif
92
93
Harald Welte57a9cf22010-05-28 16:06:53 +020094/* IPv4 messages inside the GRE tunnel might be GRE keepalives */
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +020095static int handle_rx_gre_ipv4(struct osmo_fd *bfd, struct msgb *msg,
Harald Welte57a9cf22010-05-28 16:06:53 +020096 struct iphdr *iph, struct gre_hdr *greh)
97{
98 struct gprs_ns_inst *nsi = bfd->data;
99 int gre_payload_len;
100 struct iphdr *inner_iph;
101 struct gre_hdr *inner_greh;
102 struct sockaddr_in daddr;
103 struct in_addr ia;
104
105 gre_payload_len = msg->len - (iph->ihl*4 + sizeof(*greh));
106
Harald Welte9681ce32010-05-31 11:02:57 +0200107 inner_iph = (struct iphdr *) ((uint8_t *)greh + sizeof(*greh));
Harald Welte57a9cf22010-05-28 16:06:53 +0200108
109 if (gre_payload_len < inner_iph->ihl*4 + sizeof(*inner_greh)) {
110 LOGP(DNS, LOGL_ERROR, "GRE keepalive too short\n");
111 return -EIO;
112 }
113
114 if (inner_iph->saddr != iph->daddr ||
115 inner_iph->daddr != iph->saddr) {
116 LOGP(DNS, LOGL_ERROR,
117 "GRE keepalive with wrong tunnel addresses\n");
118 return -EIO;
119 }
120
121 if (inner_iph->protocol != IPPROTO_GRE) {
122 LOGP(DNS, LOGL_ERROR, "GRE keepalive with wrong protocol\n");
123 return -EIO;
124 }
125
Harald Welte9681ce32010-05-31 11:02:57 +0200126 inner_greh = (struct gre_hdr *) ((uint8_t *)inner_iph + iph->ihl*4);
Harald Weltebfe62e52017-05-15 12:48:30 +0200127 if (inner_greh->ptype != osmo_htons(GRE_PTYPE_KAR)) {
Harald Welte57a9cf22010-05-28 16:06:53 +0200128 LOGP(DNS, LOGL_ERROR, "GRE keepalive inner GRE type != 0\n");
129 return -EIO;
130 }
131
132 /* Actually send the response back */
133
134 daddr.sin_family = AF_INET;
135 daddr.sin_addr.s_addr = inner_iph->daddr;
136 daddr.sin_port = IPPROTO_GRE;
137
138 ia.s_addr = iph->saddr;
139 LOGP(DNS, LOGL_DEBUG, "GRE keepalive from %s, responding\n",
140 inet_ntoa(ia));
141
142 return sendto(nsi->frgre.fd.fd, inner_greh,
143 gre_payload_len - inner_iph->ihl*4, 0,
144 (struct sockaddr *)&daddr, sizeof(daddr));
145}
146
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200147static struct msgb *read_nsfrgre_msg(struct osmo_fd *bfd, int *error,
Harald Welteb3ee2652010-05-19 14:38:50 +0200148 struct sockaddr_in *saddr)
149{
150 struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
151 int ret = 0;
152 socklen_t saddr_len = sizeof(*saddr);
Harald Welte6c4136e2010-05-19 15:53:22 +0200153 struct iphdr *iph;
Harald Welteb3ee2652010-05-19 14:38:50 +0200154 struct gre_hdr *greh;
155 uint8_t *frh;
Harald Welte869aaa42010-05-19 16:48:12 +0200156 uint16_t dlci;
Harald Welteb3ee2652010-05-19 14:38:50 +0200157
158 if (!msg) {
159 *error = -ENOMEM;
160 return NULL;
161 }
162
163 ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0,
164 (struct sockaddr *)saddr, &saddr_len);
165 if (ret < 0) {
166 LOGP(DNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n",
167 strerror(errno));
168 *error = ret;
Harald Welteb11226d2010-05-19 15:46:49 +0200169 goto out_err;
Harald Welteb3ee2652010-05-19 14:38:50 +0200170 } else if (ret == 0) {
Harald Welteb3ee2652010-05-19 14:38:50 +0200171 *error = ret;
Harald Welteb11226d2010-05-19 15:46:49 +0200172 goto out_err;
Harald Welteb3ee2652010-05-19 14:38:50 +0200173 }
174
175 msgb_put(msg, ret);
176
Harald Welte6c4136e2010-05-19 15:53:22 +0200177 if (msg->len < sizeof(*iph) + sizeof(*greh) + 2) {
178 LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
Harald Welteb3ee2652010-05-19 14:38:50 +0200179 *error = -EIO;
180 goto out_err;
181 }
182
Harald Welte6c4136e2010-05-19 15:53:22 +0200183 iph = (struct iphdr *) msg->data;
184 if (msg->len < (iph->ihl*4 + sizeof(*greh) + 2)) {
185 LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
186 *error = -EIO;
187 goto out_err;
188 }
189
190 greh = (struct gre_hdr *) (msg->data + iph->ihl*4);
Harald Welteb3ee2652010-05-19 14:38:50 +0200191 if (greh->flags) {
192 LOGP(DNS, LOGL_NOTICE, "Unknown GRE flags 0x%04x\n",
Harald Weltebfe62e52017-05-15 12:48:30 +0200193 osmo_ntohs(greh->flags));
Harald Welteb3ee2652010-05-19 14:38:50 +0200194 }
Harald Welte57a9cf22010-05-28 16:06:53 +0200195
Harald Weltebfe62e52017-05-15 12:48:30 +0200196 switch (osmo_ntohs(greh->ptype)) {
Harald Welte57a9cf22010-05-28 16:06:53 +0200197 case GRE_PTYPE_IPv4:
198 /* IPv4 messages might be GRE keepalives */
199 *error = handle_rx_gre_ipv4(bfd, msg, iph, greh);
200 goto out_err;
201 break;
202 case GRE_PTYPE_FR:
203 /* continue as usual */
204 break;
205 default:
Harald Welteb3ee2652010-05-19 14:38:50 +0200206 LOGP(DNS, LOGL_NOTICE, "Unknown GRE protocol 0x%04x != FR\n",
Harald Weltebfe62e52017-05-15 12:48:30 +0200207 osmo_ntohs(greh->ptype));
Harald Welteb3ee2652010-05-19 14:38:50 +0200208 *error = -EIO;
209 goto out_err;
Harald Welte57a9cf22010-05-28 16:06:53 +0200210 break;
Harald Welteb3ee2652010-05-19 14:38:50 +0200211 }
212
213 if (msg->len < sizeof(*greh) + 2) {
214 LOGP(DNS, LOGL_ERROR, "Short FR header: %u bytes\n", msg->len);
215 *error = -EIO;
216 goto out_err;
217 }
218
Harald Welte6c4136e2010-05-19 15:53:22 +0200219 frh = (uint8_t *)greh + sizeof(*greh);
Harald Welteb3ee2652010-05-19 14:38:50 +0200220 if (frh[0] & 0x01) {
221 LOGP(DNS, LOGL_NOTICE, "Unsupported single-byte FR address\n");
222 *error = -EIO;
223 goto out_err;
224 }
Harald Welte869aaa42010-05-19 16:48:12 +0200225 dlci = ((frh[0] & 0xfc) << 2);
Harald Welteb3ee2652010-05-19 14:38:50 +0200226 if ((frh[1] & 0x0f) != 0x01) {
227 LOGP(DNS, LOGL_NOTICE, "Unknown second FR octet 0x%02x\n",
228 frh[1]);
229 *error = -EIO;
230 goto out_err;
231 }
Harald Welte869aaa42010-05-19 16:48:12 +0200232 dlci |= (frh[1] >> 4);
Harald Welteb3ee2652010-05-19 14:38:50 +0200233
Harald Welte6c4136e2010-05-19 15:53:22 +0200234 msg->l2h = frh+2;
Harald Welteb3ee2652010-05-19 14:38:50 +0200235
236 /* Store DLCI in NETWORK BYTEORDER in sockaddr port member */
Harald Weltebfe62e52017-05-15 12:48:30 +0200237 saddr->sin_port = osmo_htons(dlci);
Harald Welteb3ee2652010-05-19 14:38:50 +0200238
239 return msg;
240
241out_err:
242 msgb_free(msg);
243 return NULL;
244}
245
246int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
247 struct sockaddr_in *saddr, enum gprs_ns_ll ll);
248
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200249static int handle_nsfrgre_read(struct osmo_fd *bfd)
Harald Welteb3ee2652010-05-19 14:38:50 +0200250{
Harald Weltef15497c2010-05-28 16:12:57 +0200251 int rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200252 struct sockaddr_in saddr;
253 struct gprs_ns_inst *nsi = bfd->data;
Harald Weltef15497c2010-05-28 16:12:57 +0200254 struct msgb *msg;
255 uint16_t dlci;
Harald Welteb3ee2652010-05-19 14:38:50 +0200256
Harald Weltef15497c2010-05-28 16:12:57 +0200257 msg = read_nsfrgre_msg(bfd, &rc, &saddr);
Harald Welteb3ee2652010-05-19 14:38:50 +0200258 if (!msg)
Harald Weltef15497c2010-05-28 16:12:57 +0200259 return rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200260
Harald Weltebfe62e52017-05-15 12:48:30 +0200261 dlci = osmo_ntohs(saddr.sin_port);
Harald Weltef15497c2010-05-28 16:12:57 +0200262 if (dlci == 0 || dlci == 1023) {
263 LOGP(DNS, LOGL_INFO, "Received FR on LMI DLCI %u - ignoring\n",
264 dlci);
265 rc = 0;
266 goto out;
267 }
Harald Welteb3ee2652010-05-19 14:38:50 +0200268
Harald Weltef15497c2010-05-28 16:12:57 +0200269 rc = gprs_ns_rcvmsg(nsi, msg, &saddr, GPRS_NS_LL_FR_GRE);
270out:
Harald Welteb3ee2652010-05-19 14:38:50 +0200271 msgb_free(msg);
272
Harald Weltef15497c2010-05-28 16:12:57 +0200273 return rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200274}
275
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200276static int handle_nsfrgre_write(struct osmo_fd *bfd)
Harald Welteb3ee2652010-05-19 14:38:50 +0200277{
278 /* FIXME: actually send the data here instead of nsip_sendmsg() */
279 return -EIO;
280}
281
282int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg)
283{
284 int rc;
285 struct gprs_ns_inst *nsi = nsvc->nsi;
286 struct sockaddr_in daddr;
Harald Weltebfe62e52017-05-15 12:48:30 +0200287 uint16_t dlci = osmo_ntohs(nsvc->frgre.bts_addr.sin_port);
Harald Welteb3ee2652010-05-19 14:38:50 +0200288 uint8_t *frh;
289 struct gre_hdr *greh;
290
291 /* Build socket address for the packet destionation */
Harald Welteac914b82010-05-19 15:37:34 +0200292 daddr.sin_family = AF_INET;
Harald Welteb3ee2652010-05-19 14:38:50 +0200293 daddr.sin_addr = nsvc->frgre.bts_addr.sin_addr;
294 daddr.sin_port = IPPROTO_GRE;
295
296 /* Prepend the FR header */
Harald Welteac914b82010-05-19 15:37:34 +0200297 frh = msgb_push(msg, 2);
Harald Welteb3ee2652010-05-19 14:38:50 +0200298 frh[0] = (dlci >> 2) & 0xfc;
Harald Welteac914b82010-05-19 15:37:34 +0200299 frh[1] = ((dlci & 0xf)<<4) | 0x01;
Harald Welteb3ee2652010-05-19 14:38:50 +0200300
301 /* Prepend the GRE header */
302 greh = (struct gre_hdr *) msgb_push(msg, sizeof(*greh));
303 greh->flags = 0;
Harald Weltebfe62e52017-05-15 12:48:30 +0200304 greh->ptype = osmo_htons(GRE_PTYPE_FR);
Harald Welteb3ee2652010-05-19 14:38:50 +0200305
306 rc = sendto(nsi->frgre.fd.fd, msg->data, msg->len, 0,
307 (struct sockaddr *)&daddr, sizeof(daddr));
308
Holger Hans Peter Freyther52746462012-03-01 20:30:32 +0100309 msgb_free(msg);
Harald Welteb3ee2652010-05-19 14:38:50 +0200310
311 return rc;
312}
313
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200314static int nsfrgre_fd_cb(struct osmo_fd *bfd, unsigned int what)
Harald Welteb3ee2652010-05-19 14:38:50 +0200315{
316 int rc = 0;
317
Harald Welte16886992019-03-20 10:26:39 +0100318 if (what & OSMO_FD_READ)
Harald Welteb3ee2652010-05-19 14:38:50 +0200319 rc = handle_nsfrgre_read(bfd);
Harald Welte16886992019-03-20 10:26:39 +0100320 if (what & OSMO_FD_WRITE)
Harald Welteb3ee2652010-05-19 14:38:50 +0200321 rc = handle_nsfrgre_write(bfd);
322
323 return rc;
324}
325
Harald Welte7fb05232010-05-19 15:09:09 +0200326int gprs_ns_frgre_listen(struct gprs_ns_inst *nsi)
Harald Welteb3ee2652010-05-19 14:38:50 +0200327{
Harald Welte73952e32012-06-16 14:59:56 +0800328 struct in_addr in;
Harald Welteb3ee2652010-05-19 14:38:50 +0200329 int rc;
330
Harald Weltebfe62e52017-05-15 12:48:30 +0200331 in.s_addr = osmo_htonl(nsi->frgre.local_ip);
Harald Welte73952e32012-06-16 14:59:56 +0800332
Harald Welteb3ee2652010-05-19 14:38:50 +0200333 /* Make sure we close any existing socket before changing it */
334 if (nsi->frgre.fd.fd)
335 close(nsi->frgre.fd.fd);
336
Harald Welte7fb05232010-05-19 15:09:09 +0200337 if (!nsi->frgre.enabled)
338 return 0;
339
Harald Welte73952e32012-06-16 14:59:56 +0800340 nsi->frgre.fd.cb = nsfrgre_fd_cb;
341 nsi->frgre.fd.data = nsi;
342 rc = osmo_sock_init_ofd(&nsi->frgre.fd, AF_INET, SOCK_RAW,
343 IPPROTO_GRE, inet_ntoa(in), 0,
344 OSMO_SOCK_F_BIND);
Harald Welteb3ee2652010-05-19 14:38:50 +0200345 if (rc < 0) {
346 LOGP(DNS, LOGL_ERROR, "Error creating GRE socket (%s)\n",
347 strerror(errno));
348 return rc;
349 }
350 nsi->frgre.fd.data = nsi;
351
352 return rc;
353}