blob: 52145ff6b367ae8f27ce418d921eb35568884788 [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/*
6 * (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
Harald Welteb3ee2652010-05-19 14:38:50 +02007 *
8 * All Rights Reserved
9 *
10 * This program is free software; you can redistribute it and/or modify
Harald Welte7fa89c22014-10-26 20:33:09 +010011 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
Harald Welteb3ee2652010-05-19 14:38:50 +020013 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
Harald Welte7fa89c22014-10-26 20:33:09 +010018 * GNU General Public License for more details.
Harald Welteb3ee2652010-05-19 14:38:50 +020019 *
Harald Welte7fa89c22014-10-26 20:33:09 +010020 * You should have received a copy of the GNU General Public License
Harald Weltee4cbb3f2011-01-01 15:25:50 +010021 * along with this program. If not, see <http://www.gnu.org/licenses/>.
Harald Welteb3ee2652010-05-19 14:38:50 +020022 *
23 */
24
25#include <errno.h>
26#include <string.h>
27#include <unistd.h>
28
29#include <sys/socket.h>
30#include <netinet/in.h>
Harald Welte6c4136e2010-05-19 15:53:22 +020031#include <netinet/ip.h>
Harald Welteb3ee2652010-05-19 14:38:50 +020032#include <arpa/inet.h>
33
Pablo Neira Ayusoff663232011-03-22 16:47:59 +010034#include <osmocom/core/select.h>
Harald Weltebfe62e52017-05-15 12:48:30 +020035#include <osmocom/core/byteswap.h>
Pablo Neira Ayusoff663232011-03-22 16:47:59 +010036#include <osmocom/core/msgb.h>
37#include <osmocom/core/talloc.h>
Harald Welte73952e32012-06-16 14:59:56 +080038#include <osmocom/core/socket.h>
39#include <osmocom/gprs/gprs_ns.h>
Harald Welteb3ee2652010-05-19 14:38:50 +020040
Harald Weltecca49632012-06-16 17:45:59 +080041#include "common_vty.h"
Harald Welteb3ee2652010-05-19 14:38:50 +020042
43#define GRE_PTYPE_FR 0x6559
Harald Welte57a9cf22010-05-28 16:06:53 +020044#define GRE_PTYPE_IPv4 0x0800
45#define GRE_PTYPE_KAR 0x0000 /* keepalive response */
Harald Welteb3ee2652010-05-19 14:38:50 +020046
Evgeny Zverev06ddf8b2013-01-12 00:02:40 +040047#ifndef IPPROTO_GRE
48# define IPPROTO_GRE 47
49#endif
50
Harald Welteb3ee2652010-05-19 14:38:50 +020051struct gre_hdr {
52 uint16_t flags;
53 uint16_t ptype;
54} __attribute__ ((packed));
55
Evgeny Zverev06ddf8b2013-01-12 00:02:40 +040056#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__CYGWIN__)
Holger Hans Peter Freyther3c16de22012-07-12 14:13:41 +020057/**
58 * On BSD the IPv4 struct is called struct ip and instead of iXX
59 * the members are called ip_XX. One could change this code to use
60 * struct ip but that would require to define _BSD_SOURCE and that
61 * might have other complications. Instead make sure struct iphdr
62 * is present on FreeBSD. The below is taken from GLIBC.
63 *
64 * The GNU C Library is free software; you can redistribute it and/or
65 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
67 * version 2.1 of the License, or (at your option) any later version.
68 */
69struct iphdr
70 {
71#if BYTE_ORDER == LITTLE_ENDIAN
72 unsigned int ihl:4;
73 unsigned int version:4;
74#elif BYTE_ORDER == BIG_ENDIAN
75 unsigned int version:4;
76 unsigned int ihl:4;
77#endif
78 u_int8_t tos;
79 u_int16_t tot_len;
80 u_int16_t id;
81 u_int16_t frag_off;
82 u_int8_t ttl;
83 u_int8_t protocol;
84 u_int16_t check;
85 u_int32_t saddr;
86 u_int32_t daddr;
87 /*The options start here. */
88 };
89#endif
90
91
Harald Welte57a9cf22010-05-28 16:06:53 +020092/* IPv4 messages inside the GRE tunnel might be GRE keepalives */
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +020093static int handle_rx_gre_ipv4(struct osmo_fd *bfd, struct msgb *msg,
Harald Welte57a9cf22010-05-28 16:06:53 +020094 struct iphdr *iph, struct gre_hdr *greh)
95{
96 struct gprs_ns_inst *nsi = bfd->data;
97 int gre_payload_len;
98 struct iphdr *inner_iph;
99 struct gre_hdr *inner_greh;
100 struct sockaddr_in daddr;
101 struct in_addr ia;
102
103 gre_payload_len = msg->len - (iph->ihl*4 + sizeof(*greh));
104
Harald Welte9681ce32010-05-31 11:02:57 +0200105 inner_iph = (struct iphdr *) ((uint8_t *)greh + sizeof(*greh));
Harald Welte57a9cf22010-05-28 16:06:53 +0200106
107 if (gre_payload_len < inner_iph->ihl*4 + sizeof(*inner_greh)) {
108 LOGP(DNS, LOGL_ERROR, "GRE keepalive too short\n");
109 return -EIO;
110 }
111
112 if (inner_iph->saddr != iph->daddr ||
113 inner_iph->daddr != iph->saddr) {
114 LOGP(DNS, LOGL_ERROR,
115 "GRE keepalive with wrong tunnel addresses\n");
116 return -EIO;
117 }
118
119 if (inner_iph->protocol != IPPROTO_GRE) {
120 LOGP(DNS, LOGL_ERROR, "GRE keepalive with wrong protocol\n");
121 return -EIO;
122 }
123
Harald Welte9681ce32010-05-31 11:02:57 +0200124 inner_greh = (struct gre_hdr *) ((uint8_t *)inner_iph + iph->ihl*4);
Harald Weltebfe62e52017-05-15 12:48:30 +0200125 if (inner_greh->ptype != osmo_htons(GRE_PTYPE_KAR)) {
Harald Welte57a9cf22010-05-28 16:06:53 +0200126 LOGP(DNS, LOGL_ERROR, "GRE keepalive inner GRE type != 0\n");
127 return -EIO;
128 }
129
130 /* Actually send the response back */
131
132 daddr.sin_family = AF_INET;
133 daddr.sin_addr.s_addr = inner_iph->daddr;
134 daddr.sin_port = IPPROTO_GRE;
135
136 ia.s_addr = iph->saddr;
137 LOGP(DNS, LOGL_DEBUG, "GRE keepalive from %s, responding\n",
138 inet_ntoa(ia));
139
140 return sendto(nsi->frgre.fd.fd, inner_greh,
141 gre_payload_len - inner_iph->ihl*4, 0,
142 (struct sockaddr *)&daddr, sizeof(daddr));
143}
144
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200145static struct msgb *read_nsfrgre_msg(struct osmo_fd *bfd, int *error,
Harald Welteb3ee2652010-05-19 14:38:50 +0200146 struct sockaddr_in *saddr)
147{
148 struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
149 int ret = 0;
150 socklen_t saddr_len = sizeof(*saddr);
Harald Welte6c4136e2010-05-19 15:53:22 +0200151 struct iphdr *iph;
Harald Welteb3ee2652010-05-19 14:38:50 +0200152 struct gre_hdr *greh;
153 uint8_t *frh;
Harald Welte869aaa42010-05-19 16:48:12 +0200154 uint16_t dlci;
Harald Welteb3ee2652010-05-19 14:38:50 +0200155
156 if (!msg) {
157 *error = -ENOMEM;
158 return NULL;
159 }
160
161 ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0,
162 (struct sockaddr *)saddr, &saddr_len);
163 if (ret < 0) {
164 LOGP(DNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n",
165 strerror(errno));
166 *error = ret;
Harald Welteb11226d2010-05-19 15:46:49 +0200167 goto out_err;
Harald Welteb3ee2652010-05-19 14:38:50 +0200168 } else if (ret == 0) {
Harald Welteb3ee2652010-05-19 14:38:50 +0200169 *error = ret;
Harald Welteb11226d2010-05-19 15:46:49 +0200170 goto out_err;
Harald Welteb3ee2652010-05-19 14:38:50 +0200171 }
172
173 msgb_put(msg, ret);
174
Harald Welte6c4136e2010-05-19 15:53:22 +0200175 if (msg->len < sizeof(*iph) + sizeof(*greh) + 2) {
176 LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
Harald Welteb3ee2652010-05-19 14:38:50 +0200177 *error = -EIO;
178 goto out_err;
179 }
180
Harald Welte6c4136e2010-05-19 15:53:22 +0200181 iph = (struct iphdr *) msg->data;
182 if (msg->len < (iph->ihl*4 + sizeof(*greh) + 2)) {
183 LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
184 *error = -EIO;
185 goto out_err;
186 }
187
188 greh = (struct gre_hdr *) (msg->data + iph->ihl*4);
Harald Welteb3ee2652010-05-19 14:38:50 +0200189 if (greh->flags) {
190 LOGP(DNS, LOGL_NOTICE, "Unknown GRE flags 0x%04x\n",
Harald Weltebfe62e52017-05-15 12:48:30 +0200191 osmo_ntohs(greh->flags));
Harald Welteb3ee2652010-05-19 14:38:50 +0200192 }
Harald Welte57a9cf22010-05-28 16:06:53 +0200193
Harald Weltebfe62e52017-05-15 12:48:30 +0200194 switch (osmo_ntohs(greh->ptype)) {
Harald Welte57a9cf22010-05-28 16:06:53 +0200195 case GRE_PTYPE_IPv4:
196 /* IPv4 messages might be GRE keepalives */
197 *error = handle_rx_gre_ipv4(bfd, msg, iph, greh);
198 goto out_err;
199 break;
200 case GRE_PTYPE_FR:
201 /* continue as usual */
202 break;
203 default:
Harald Welteb3ee2652010-05-19 14:38:50 +0200204 LOGP(DNS, LOGL_NOTICE, "Unknown GRE protocol 0x%04x != FR\n",
Harald Weltebfe62e52017-05-15 12:48:30 +0200205 osmo_ntohs(greh->ptype));
Harald Welteb3ee2652010-05-19 14:38:50 +0200206 *error = -EIO;
207 goto out_err;
Harald Welte57a9cf22010-05-28 16:06:53 +0200208 break;
Harald Welteb3ee2652010-05-19 14:38:50 +0200209 }
210
211 if (msg->len < sizeof(*greh) + 2) {
212 LOGP(DNS, LOGL_ERROR, "Short FR header: %u bytes\n", msg->len);
213 *error = -EIO;
214 goto out_err;
215 }
216
Harald Welte6c4136e2010-05-19 15:53:22 +0200217 frh = (uint8_t *)greh + sizeof(*greh);
Harald Welteb3ee2652010-05-19 14:38:50 +0200218 if (frh[0] & 0x01) {
219 LOGP(DNS, LOGL_NOTICE, "Unsupported single-byte FR address\n");
220 *error = -EIO;
221 goto out_err;
222 }
Harald Welte869aaa42010-05-19 16:48:12 +0200223 dlci = ((frh[0] & 0xfc) << 2);
Harald Welteb3ee2652010-05-19 14:38:50 +0200224 if ((frh[1] & 0x0f) != 0x01) {
225 LOGP(DNS, LOGL_NOTICE, "Unknown second FR octet 0x%02x\n",
226 frh[1]);
227 *error = -EIO;
228 goto out_err;
229 }
Harald Welte869aaa42010-05-19 16:48:12 +0200230 dlci |= (frh[1] >> 4);
Harald Welteb3ee2652010-05-19 14:38:50 +0200231
Harald Welte6c4136e2010-05-19 15:53:22 +0200232 msg->l2h = frh+2;
Harald Welteb3ee2652010-05-19 14:38:50 +0200233
234 /* Store DLCI in NETWORK BYTEORDER in sockaddr port member */
Harald Weltebfe62e52017-05-15 12:48:30 +0200235 saddr->sin_port = osmo_htons(dlci);
Harald Welteb3ee2652010-05-19 14:38:50 +0200236
237 return msg;
238
239out_err:
240 msgb_free(msg);
241 return NULL;
242}
243
244int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
245 struct sockaddr_in *saddr, enum gprs_ns_ll ll);
246
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200247static int handle_nsfrgre_read(struct osmo_fd *bfd)
Harald Welteb3ee2652010-05-19 14:38:50 +0200248{
Harald Weltef15497c2010-05-28 16:12:57 +0200249 int rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200250 struct sockaddr_in saddr;
251 struct gprs_ns_inst *nsi = bfd->data;
Harald Weltef15497c2010-05-28 16:12:57 +0200252 struct msgb *msg;
253 uint16_t dlci;
Harald Welteb3ee2652010-05-19 14:38:50 +0200254
Harald Weltef15497c2010-05-28 16:12:57 +0200255 msg = read_nsfrgre_msg(bfd, &rc, &saddr);
Harald Welteb3ee2652010-05-19 14:38:50 +0200256 if (!msg)
Harald Weltef15497c2010-05-28 16:12:57 +0200257 return rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200258
Harald Weltebfe62e52017-05-15 12:48:30 +0200259 dlci = osmo_ntohs(saddr.sin_port);
Harald Weltef15497c2010-05-28 16:12:57 +0200260 if (dlci == 0 || dlci == 1023) {
261 LOGP(DNS, LOGL_INFO, "Received FR on LMI DLCI %u - ignoring\n",
262 dlci);
263 rc = 0;
264 goto out;
265 }
Harald Welteb3ee2652010-05-19 14:38:50 +0200266
Harald Weltef15497c2010-05-28 16:12:57 +0200267 rc = gprs_ns_rcvmsg(nsi, msg, &saddr, GPRS_NS_LL_FR_GRE);
268out:
Harald Welteb3ee2652010-05-19 14:38:50 +0200269 msgb_free(msg);
270
Harald Weltef15497c2010-05-28 16:12:57 +0200271 return rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200272}
273
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200274static int handle_nsfrgre_write(struct osmo_fd *bfd)
Harald Welteb3ee2652010-05-19 14:38:50 +0200275{
276 /* FIXME: actually send the data here instead of nsip_sendmsg() */
277 return -EIO;
278}
279
280int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg)
281{
282 int rc;
283 struct gprs_ns_inst *nsi = nsvc->nsi;
284 struct sockaddr_in daddr;
Harald Weltebfe62e52017-05-15 12:48:30 +0200285 uint16_t dlci = osmo_ntohs(nsvc->frgre.bts_addr.sin_port);
Harald Welteb3ee2652010-05-19 14:38:50 +0200286 uint8_t *frh;
287 struct gre_hdr *greh;
288
289 /* Build socket address for the packet destionation */
Harald Welteac914b82010-05-19 15:37:34 +0200290 daddr.sin_family = AF_INET;
Harald Welteb3ee2652010-05-19 14:38:50 +0200291 daddr.sin_addr = nsvc->frgre.bts_addr.sin_addr;
292 daddr.sin_port = IPPROTO_GRE;
293
294 /* Prepend the FR header */
Harald Welteac914b82010-05-19 15:37:34 +0200295 frh = msgb_push(msg, 2);
Harald Welteb3ee2652010-05-19 14:38:50 +0200296 frh[0] = (dlci >> 2) & 0xfc;
Harald Welteac914b82010-05-19 15:37:34 +0200297 frh[1] = ((dlci & 0xf)<<4) | 0x01;
Harald Welteb3ee2652010-05-19 14:38:50 +0200298
299 /* Prepend the GRE header */
300 greh = (struct gre_hdr *) msgb_push(msg, sizeof(*greh));
301 greh->flags = 0;
Harald Weltebfe62e52017-05-15 12:48:30 +0200302 greh->ptype = osmo_htons(GRE_PTYPE_FR);
Harald Welteb3ee2652010-05-19 14:38:50 +0200303
304 rc = sendto(nsi->frgre.fd.fd, msg->data, msg->len, 0,
305 (struct sockaddr *)&daddr, sizeof(daddr));
306
Holger Hans Peter Freyther52746462012-03-01 20:30:32 +0100307 msgb_free(msg);
Harald Welteb3ee2652010-05-19 14:38:50 +0200308
309 return rc;
310}
311
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200312static int nsfrgre_fd_cb(struct osmo_fd *bfd, unsigned int what)
Harald Welteb3ee2652010-05-19 14:38:50 +0200313{
314 int rc = 0;
315
316 if (what & BSC_FD_READ)
317 rc = handle_nsfrgre_read(bfd);
318 if (what & BSC_FD_WRITE)
319 rc = handle_nsfrgre_write(bfd);
320
321 return rc;
322}
323
Harald Welte7fb05232010-05-19 15:09:09 +0200324int gprs_ns_frgre_listen(struct gprs_ns_inst *nsi)
Harald Welteb3ee2652010-05-19 14:38:50 +0200325{
Harald Welte73952e32012-06-16 14:59:56 +0800326 struct in_addr in;
Harald Welteb3ee2652010-05-19 14:38:50 +0200327 int rc;
328
Harald Weltebfe62e52017-05-15 12:48:30 +0200329 in.s_addr = osmo_htonl(nsi->frgre.local_ip);
Harald Welte73952e32012-06-16 14:59:56 +0800330
Harald Welteb3ee2652010-05-19 14:38:50 +0200331 /* Make sure we close any existing socket before changing it */
332 if (nsi->frgre.fd.fd)
333 close(nsi->frgre.fd.fd);
334
Harald Welte7fb05232010-05-19 15:09:09 +0200335 if (!nsi->frgre.enabled)
336 return 0;
337
Harald Welte73952e32012-06-16 14:59:56 +0800338 nsi->frgre.fd.cb = nsfrgre_fd_cb;
339 nsi->frgre.fd.data = nsi;
340 rc = osmo_sock_init_ofd(&nsi->frgre.fd, AF_INET, SOCK_RAW,
341 IPPROTO_GRE, inet_ntoa(in), 0,
342 OSMO_SOCK_F_BIND);
Harald Welteb3ee2652010-05-19 14:38:50 +0200343 if (rc < 0) {
344 LOGP(DNS, LOGL_ERROR, "Error creating GRE socket (%s)\n",
345 strerror(errno));
346 return rc;
347 }
348 nsi->frgre.fd.data = nsi;
349
350 return rc;
351}