blob: 57cf65e33005046a3be7e0c4005d52b22e5e08cd [file] [log] [blame]
Harald Welteb3ee2652010-05-19 14:38:50 +02001/* GPRS Networks Service (NS) messages on the Gb interface
2 * 3GPP TS 08.16 version 8.0.1 Release 1999 / ETSI TS 101 299 V8.0.1 (2002-05) */
3
4/* NS-over-FR-over-GRE implementation */
5
6/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org>
7 *
8 * All Rights Reserved
9 *
10 * This program is free software; you can redistribute it and/or modify
Harald Weltee4cbb3f2011-01-01 15:25:50 +010011 * it under the terms of the GNU Affero General Public License as published by
12 * the Free Software Foundation; either version 3 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 Weltee4cbb3f2011-01-01 15:25:50 +010018 * GNU Affero General Public License for more details.
Harald Welteb3ee2652010-05-19 14:38:50 +020019 *
Harald Weltee4cbb3f2011-01-01 15:25:50 +010020 * You should have received a copy of the GNU Affero General Public License
21 * 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>
35#include <osmocom/core/msgb.h>
36#include <osmocom/core/talloc.h>
Harald Welte73952e32012-06-16 14:59:56 +080037#include <osmocom/core/socket.h>
38#include <osmocom/gprs/gprs_ns.h>
Harald Welteb3ee2652010-05-19 14:38:50 +020039
Harald Weltecca49632012-06-16 17:45:59 +080040#include "common_vty.h"
Harald Welteb3ee2652010-05-19 14:38:50 +020041
42#define GRE_PTYPE_FR 0x6559
Harald Welte57a9cf22010-05-28 16:06:53 +020043#define GRE_PTYPE_IPv4 0x0800
44#define GRE_PTYPE_KAR 0x0000 /* keepalive response */
Harald Welteb3ee2652010-05-19 14:38:50 +020045
Evgeny Zverev06ddf8b2013-01-12 00:02:40 +040046#ifndef IPPROTO_GRE
47# define IPPROTO_GRE 47
48#endif
49
Harald Welteb3ee2652010-05-19 14:38:50 +020050struct gre_hdr {
51 uint16_t flags;
52 uint16_t ptype;
53} __attribute__ ((packed));
54
Evgeny Zverev06ddf8b2013-01-12 00:02:40 +040055#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__CYGWIN__)
Holger Hans Peter Freyther3c16de22012-07-12 14:13:41 +020056/**
57 * On BSD the IPv4 struct is called struct ip and instead of iXX
58 * the members are called ip_XX. One could change this code to use
59 * struct ip but that would require to define _BSD_SOURCE and that
60 * might have other complications. Instead make sure struct iphdr
61 * is present on FreeBSD. The below is taken from GLIBC.
62 *
63 * The GNU C Library is free software; you can redistribute it and/or
64 * modify it under the terms of the GNU Lesser General Public
65 * License as published by the Free Software Foundation; either
66 * version 2.1 of the License, or (at your option) any later version.
67 */
68struct iphdr
69 {
70#if BYTE_ORDER == LITTLE_ENDIAN
71 unsigned int ihl:4;
72 unsigned int version:4;
73#elif BYTE_ORDER == BIG_ENDIAN
74 unsigned int version:4;
75 unsigned int ihl:4;
76#endif
77 u_int8_t tos;
78 u_int16_t tot_len;
79 u_int16_t id;
80 u_int16_t frag_off;
81 u_int8_t ttl;
82 u_int8_t protocol;
83 u_int16_t check;
84 u_int32_t saddr;
85 u_int32_t daddr;
86 /*The options start here. */
87 };
88#endif
89
90
Harald Welte57a9cf22010-05-28 16:06:53 +020091/* IPv4 messages inside the GRE tunnel might be GRE keepalives */
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +020092static int handle_rx_gre_ipv4(struct osmo_fd *bfd, struct msgb *msg,
Harald Welte57a9cf22010-05-28 16:06:53 +020093 struct iphdr *iph, struct gre_hdr *greh)
94{
95 struct gprs_ns_inst *nsi = bfd->data;
96 int gre_payload_len;
97 struct iphdr *inner_iph;
98 struct gre_hdr *inner_greh;
99 struct sockaddr_in daddr;
100 struct in_addr ia;
101
102 gre_payload_len = msg->len - (iph->ihl*4 + sizeof(*greh));
103
Harald Welte9681ce32010-05-31 11:02:57 +0200104 inner_iph = (struct iphdr *) ((uint8_t *)greh + sizeof(*greh));
Harald Welte57a9cf22010-05-28 16:06:53 +0200105
106 if (gre_payload_len < inner_iph->ihl*4 + sizeof(*inner_greh)) {
107 LOGP(DNS, LOGL_ERROR, "GRE keepalive too short\n");
108 return -EIO;
109 }
110
111 if (inner_iph->saddr != iph->daddr ||
112 inner_iph->daddr != iph->saddr) {
113 LOGP(DNS, LOGL_ERROR,
114 "GRE keepalive with wrong tunnel addresses\n");
115 return -EIO;
116 }
117
118 if (inner_iph->protocol != IPPROTO_GRE) {
119 LOGP(DNS, LOGL_ERROR, "GRE keepalive with wrong protocol\n");
120 return -EIO;
121 }
122
Harald Welte9681ce32010-05-31 11:02:57 +0200123 inner_greh = (struct gre_hdr *) ((uint8_t *)inner_iph + iph->ihl*4);
Harald Welte57a9cf22010-05-28 16:06:53 +0200124 if (inner_greh->ptype != htons(GRE_PTYPE_KAR)) {
125 LOGP(DNS, LOGL_ERROR, "GRE keepalive inner GRE type != 0\n");
126 return -EIO;
127 }
128
129 /* Actually send the response back */
130
131 daddr.sin_family = AF_INET;
132 daddr.sin_addr.s_addr = inner_iph->daddr;
133 daddr.sin_port = IPPROTO_GRE;
134
135 ia.s_addr = iph->saddr;
136 LOGP(DNS, LOGL_DEBUG, "GRE keepalive from %s, responding\n",
137 inet_ntoa(ia));
138
139 return sendto(nsi->frgre.fd.fd, inner_greh,
140 gre_payload_len - inner_iph->ihl*4, 0,
141 (struct sockaddr *)&daddr, sizeof(daddr));
142}
143
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200144static struct msgb *read_nsfrgre_msg(struct osmo_fd *bfd, int *error,
Harald Welteb3ee2652010-05-19 14:38:50 +0200145 struct sockaddr_in *saddr)
146{
147 struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
148 int ret = 0;
149 socklen_t saddr_len = sizeof(*saddr);
Harald Welte6c4136e2010-05-19 15:53:22 +0200150 struct iphdr *iph;
Harald Welteb3ee2652010-05-19 14:38:50 +0200151 struct gre_hdr *greh;
152 uint8_t *frh;
Harald Welte869aaa42010-05-19 16:48:12 +0200153 uint16_t dlci;
Harald Welteb3ee2652010-05-19 14:38:50 +0200154
155 if (!msg) {
156 *error = -ENOMEM;
157 return NULL;
158 }
159
160 ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0,
161 (struct sockaddr *)saddr, &saddr_len);
162 if (ret < 0) {
163 LOGP(DNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n",
164 strerror(errno));
165 *error = ret;
Harald Welteb11226d2010-05-19 15:46:49 +0200166 goto out_err;
Harald Welteb3ee2652010-05-19 14:38:50 +0200167 } else if (ret == 0) {
Harald Welteb3ee2652010-05-19 14:38:50 +0200168 *error = ret;
Harald Welteb11226d2010-05-19 15:46:49 +0200169 goto out_err;
Harald Welteb3ee2652010-05-19 14:38:50 +0200170 }
171
172 msgb_put(msg, ret);
173
Harald Welte6c4136e2010-05-19 15:53:22 +0200174 if (msg->len < sizeof(*iph) + sizeof(*greh) + 2) {
175 LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
Harald Welteb3ee2652010-05-19 14:38:50 +0200176 *error = -EIO;
177 goto out_err;
178 }
179
Harald Welte6c4136e2010-05-19 15:53:22 +0200180 iph = (struct iphdr *) msg->data;
181 if (msg->len < (iph->ihl*4 + sizeof(*greh) + 2)) {
182 LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
183 *error = -EIO;
184 goto out_err;
185 }
186
187 greh = (struct gre_hdr *) (msg->data + iph->ihl*4);
Harald Welteb3ee2652010-05-19 14:38:50 +0200188 if (greh->flags) {
189 LOGP(DNS, LOGL_NOTICE, "Unknown GRE flags 0x%04x\n",
190 ntohs(greh->flags));
191 }
Harald Welte57a9cf22010-05-28 16:06:53 +0200192
193 switch (ntohs(greh->ptype)) {
194 case GRE_PTYPE_IPv4:
195 /* IPv4 messages might be GRE keepalives */
196 *error = handle_rx_gre_ipv4(bfd, msg, iph, greh);
197 goto out_err;
198 break;
199 case GRE_PTYPE_FR:
200 /* continue as usual */
201 break;
202 default:
Harald Welteb3ee2652010-05-19 14:38:50 +0200203 LOGP(DNS, LOGL_NOTICE, "Unknown GRE protocol 0x%04x != FR\n",
204 ntohs(greh->ptype));
205 *error = -EIO;
206 goto out_err;
Harald Welte57a9cf22010-05-28 16:06:53 +0200207 break;
Harald Welteb3ee2652010-05-19 14:38:50 +0200208 }
209
210 if (msg->len < sizeof(*greh) + 2) {
211 LOGP(DNS, LOGL_ERROR, "Short FR header: %u bytes\n", msg->len);
212 *error = -EIO;
213 goto out_err;
214 }
215
Harald Welte6c4136e2010-05-19 15:53:22 +0200216 frh = (uint8_t *)greh + sizeof(*greh);
Harald Welteb3ee2652010-05-19 14:38:50 +0200217 if (frh[0] & 0x01) {
218 LOGP(DNS, LOGL_NOTICE, "Unsupported single-byte FR address\n");
219 *error = -EIO;
220 goto out_err;
221 }
Harald Welte869aaa42010-05-19 16:48:12 +0200222 dlci = ((frh[0] & 0xfc) << 2);
Harald Welteb3ee2652010-05-19 14:38:50 +0200223 if ((frh[1] & 0x0f) != 0x01) {
224 LOGP(DNS, LOGL_NOTICE, "Unknown second FR octet 0x%02x\n",
225 frh[1]);
226 *error = -EIO;
227 goto out_err;
228 }
Harald Welte869aaa42010-05-19 16:48:12 +0200229 dlci |= (frh[1] >> 4);
Harald Welteb3ee2652010-05-19 14:38:50 +0200230
Harald Welte6c4136e2010-05-19 15:53:22 +0200231 msg->l2h = frh+2;
Harald Welteb3ee2652010-05-19 14:38:50 +0200232
233 /* Store DLCI in NETWORK BYTEORDER in sockaddr port member */
Harald Welte869aaa42010-05-19 16:48:12 +0200234 saddr->sin_port = htons(dlci);
Harald Welteb3ee2652010-05-19 14:38:50 +0200235
236 return msg;
237
238out_err:
239 msgb_free(msg);
240 return NULL;
241}
242
243int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
244 struct sockaddr_in *saddr, enum gprs_ns_ll ll);
245
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200246static int handle_nsfrgre_read(struct osmo_fd *bfd)
Harald Welteb3ee2652010-05-19 14:38:50 +0200247{
Harald Weltef15497c2010-05-28 16:12:57 +0200248 int rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200249 struct sockaddr_in saddr;
250 struct gprs_ns_inst *nsi = bfd->data;
Harald Weltef15497c2010-05-28 16:12:57 +0200251 struct msgb *msg;
252 uint16_t dlci;
Harald Welteb3ee2652010-05-19 14:38:50 +0200253
Harald Weltef15497c2010-05-28 16:12:57 +0200254 msg = read_nsfrgre_msg(bfd, &rc, &saddr);
Harald Welteb3ee2652010-05-19 14:38:50 +0200255 if (!msg)
Harald Weltef15497c2010-05-28 16:12:57 +0200256 return rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200257
Harald Weltef15497c2010-05-28 16:12:57 +0200258 dlci = ntohs(saddr.sin_port);
259 if (dlci == 0 || dlci == 1023) {
260 LOGP(DNS, LOGL_INFO, "Received FR on LMI DLCI %u - ignoring\n",
261 dlci);
262 rc = 0;
263 goto out;
264 }
Harald Welteb3ee2652010-05-19 14:38:50 +0200265
Harald Weltef15497c2010-05-28 16:12:57 +0200266 rc = gprs_ns_rcvmsg(nsi, msg, &saddr, GPRS_NS_LL_FR_GRE);
267out:
Harald Welteb3ee2652010-05-19 14:38:50 +0200268 msgb_free(msg);
269
Harald Weltef15497c2010-05-28 16:12:57 +0200270 return rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200271}
272
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200273static int handle_nsfrgre_write(struct osmo_fd *bfd)
Harald Welteb3ee2652010-05-19 14:38:50 +0200274{
275 /* FIXME: actually send the data here instead of nsip_sendmsg() */
276 return -EIO;
277}
278
279int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg)
280{
281 int rc;
282 struct gprs_ns_inst *nsi = nsvc->nsi;
283 struct sockaddr_in daddr;
284 uint16_t dlci = ntohs(nsvc->frgre.bts_addr.sin_port);
285 uint8_t *frh;
286 struct gre_hdr *greh;
287
288 /* Build socket address for the packet destionation */
Harald Welteac914b82010-05-19 15:37:34 +0200289 daddr.sin_family = AF_INET;
Harald Welteb3ee2652010-05-19 14:38:50 +0200290 daddr.sin_addr = nsvc->frgre.bts_addr.sin_addr;
291 daddr.sin_port = IPPROTO_GRE;
292
293 /* Prepend the FR header */
Harald Welteac914b82010-05-19 15:37:34 +0200294 frh = msgb_push(msg, 2);
Harald Welteb3ee2652010-05-19 14:38:50 +0200295 frh[0] = (dlci >> 2) & 0xfc;
Harald Welteac914b82010-05-19 15:37:34 +0200296 frh[1] = ((dlci & 0xf)<<4) | 0x01;
Harald Welteb3ee2652010-05-19 14:38:50 +0200297
298 /* Prepend the GRE header */
299 greh = (struct gre_hdr *) msgb_push(msg, sizeof(*greh));
300 greh->flags = 0;
Harald Welteac914b82010-05-19 15:37:34 +0200301 greh->ptype = htons(GRE_PTYPE_FR);
Harald Welteb3ee2652010-05-19 14:38:50 +0200302
303 rc = sendto(nsi->frgre.fd.fd, msg->data, msg->len, 0,
304 (struct sockaddr *)&daddr, sizeof(daddr));
305
Holger Hans Peter Freyther52746462012-03-01 20:30:32 +0100306 msgb_free(msg);
Harald Welteb3ee2652010-05-19 14:38:50 +0200307
308 return rc;
309}
310
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200311static int nsfrgre_fd_cb(struct osmo_fd *bfd, unsigned int what)
Harald Welteb3ee2652010-05-19 14:38:50 +0200312{
313 int rc = 0;
314
315 if (what & BSC_FD_READ)
316 rc = handle_nsfrgre_read(bfd);
317 if (what & BSC_FD_WRITE)
318 rc = handle_nsfrgre_write(bfd);
319
320 return rc;
321}
322
Harald Welte7fb05232010-05-19 15:09:09 +0200323int gprs_ns_frgre_listen(struct gprs_ns_inst *nsi)
Harald Welteb3ee2652010-05-19 14:38:50 +0200324{
Harald Welte73952e32012-06-16 14:59:56 +0800325 struct in_addr in;
Harald Welteb3ee2652010-05-19 14:38:50 +0200326 int rc;
327
Harald Welte73952e32012-06-16 14:59:56 +0800328 in.s_addr = htonl(nsi->frgre.local_ip);
329
Harald Welteb3ee2652010-05-19 14:38:50 +0200330 /* Make sure we close any existing socket before changing it */
331 if (nsi->frgre.fd.fd)
332 close(nsi->frgre.fd.fd);
333
Harald Welte7fb05232010-05-19 15:09:09 +0200334 if (!nsi->frgre.enabled)
335 return 0;
336
Harald Welte73952e32012-06-16 14:59:56 +0800337 nsi->frgre.fd.cb = nsfrgre_fd_cb;
338 nsi->frgre.fd.data = nsi;
339 rc = osmo_sock_init_ofd(&nsi->frgre.fd, AF_INET, SOCK_RAW,
340 IPPROTO_GRE, inet_ntoa(in), 0,
341 OSMO_SOCK_F_BIND);
Harald Welteb3ee2652010-05-19 14:38:50 +0200342 if (rc < 0) {
343 LOGP(DNS, LOGL_ERROR, "Error creating GRE socket (%s)\n",
344 strerror(errno));
345 return rc;
346 }
347 nsi->frgre.fd.data = nsi;
348
349 return rc;
350}