blob: 2344381231a6e4f4b051eb3fdfed97b29f40401d [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
46struct gre_hdr {
47 uint16_t flags;
48 uint16_t ptype;
49} __attribute__ ((packed));
50
Holger Hans Peter Freyther3c16de22012-07-12 14:13:41 +020051#if defined(__FreeBSD__)
52/**
53 * On BSD the IPv4 struct is called struct ip and instead of iXX
54 * the members are called ip_XX. One could change this code to use
55 * struct ip but that would require to define _BSD_SOURCE and that
56 * might have other complications. Instead make sure struct iphdr
57 * is present on FreeBSD. The below is taken from GLIBC.
58 *
59 * The GNU C Library is free software; you can redistribute it and/or
60 * modify it under the terms of the GNU Lesser General Public
61 * License as published by the Free Software Foundation; either
62 * version 2.1 of the License, or (at your option) any later version.
63 */
64struct iphdr
65 {
66#if BYTE_ORDER == LITTLE_ENDIAN
67 unsigned int ihl:4;
68 unsigned int version:4;
69#elif BYTE_ORDER == BIG_ENDIAN
70 unsigned int version:4;
71 unsigned int ihl:4;
72#endif
73 u_int8_t tos;
74 u_int16_t tot_len;
75 u_int16_t id;
76 u_int16_t frag_off;
77 u_int8_t ttl;
78 u_int8_t protocol;
79 u_int16_t check;
80 u_int32_t saddr;
81 u_int32_t daddr;
82 /*The options start here. */
83 };
84#endif
85
86
Harald Welte57a9cf22010-05-28 16:06:53 +020087/* IPv4 messages inside the GRE tunnel might be GRE keepalives */
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +020088static int handle_rx_gre_ipv4(struct osmo_fd *bfd, struct msgb *msg,
Harald Welte57a9cf22010-05-28 16:06:53 +020089 struct iphdr *iph, struct gre_hdr *greh)
90{
91 struct gprs_ns_inst *nsi = bfd->data;
92 int gre_payload_len;
93 struct iphdr *inner_iph;
94 struct gre_hdr *inner_greh;
95 struct sockaddr_in daddr;
96 struct in_addr ia;
97
98 gre_payload_len = msg->len - (iph->ihl*4 + sizeof(*greh));
99
Harald Welte9681ce32010-05-31 11:02:57 +0200100 inner_iph = (struct iphdr *) ((uint8_t *)greh + sizeof(*greh));
Harald Welte57a9cf22010-05-28 16:06:53 +0200101
102 if (gre_payload_len < inner_iph->ihl*4 + sizeof(*inner_greh)) {
103 LOGP(DNS, LOGL_ERROR, "GRE keepalive too short\n");
104 return -EIO;
105 }
106
107 if (inner_iph->saddr != iph->daddr ||
108 inner_iph->daddr != iph->saddr) {
109 LOGP(DNS, LOGL_ERROR,
110 "GRE keepalive with wrong tunnel addresses\n");
111 return -EIO;
112 }
113
114 if (inner_iph->protocol != IPPROTO_GRE) {
115 LOGP(DNS, LOGL_ERROR, "GRE keepalive with wrong protocol\n");
116 return -EIO;
117 }
118
Harald Welte9681ce32010-05-31 11:02:57 +0200119 inner_greh = (struct gre_hdr *) ((uint8_t *)inner_iph + iph->ihl*4);
Harald Welte57a9cf22010-05-28 16:06:53 +0200120 if (inner_greh->ptype != htons(GRE_PTYPE_KAR)) {
121 LOGP(DNS, LOGL_ERROR, "GRE keepalive inner GRE type != 0\n");
122 return -EIO;
123 }
124
125 /* Actually send the response back */
126
127 daddr.sin_family = AF_INET;
128 daddr.sin_addr.s_addr = inner_iph->daddr;
129 daddr.sin_port = IPPROTO_GRE;
130
131 ia.s_addr = iph->saddr;
132 LOGP(DNS, LOGL_DEBUG, "GRE keepalive from %s, responding\n",
133 inet_ntoa(ia));
134
135 return sendto(nsi->frgre.fd.fd, inner_greh,
136 gre_payload_len - inner_iph->ihl*4, 0,
137 (struct sockaddr *)&daddr, sizeof(daddr));
138}
139
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200140static struct msgb *read_nsfrgre_msg(struct osmo_fd *bfd, int *error,
Harald Welteb3ee2652010-05-19 14:38:50 +0200141 struct sockaddr_in *saddr)
142{
143 struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
144 int ret = 0;
145 socklen_t saddr_len = sizeof(*saddr);
Harald Welte6c4136e2010-05-19 15:53:22 +0200146 struct iphdr *iph;
Harald Welteb3ee2652010-05-19 14:38:50 +0200147 struct gre_hdr *greh;
148 uint8_t *frh;
Harald Welte869aaa42010-05-19 16:48:12 +0200149 uint16_t dlci;
Harald Welteb3ee2652010-05-19 14:38:50 +0200150
151 if (!msg) {
152 *error = -ENOMEM;
153 return NULL;
154 }
155
156 ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0,
157 (struct sockaddr *)saddr, &saddr_len);
158 if (ret < 0) {
159 LOGP(DNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n",
160 strerror(errno));
161 *error = ret;
Harald Welteb11226d2010-05-19 15:46:49 +0200162 goto out_err;
Harald Welteb3ee2652010-05-19 14:38:50 +0200163 } else if (ret == 0) {
Harald Welteb3ee2652010-05-19 14:38:50 +0200164 *error = ret;
Harald Welteb11226d2010-05-19 15:46:49 +0200165 goto out_err;
Harald Welteb3ee2652010-05-19 14:38:50 +0200166 }
167
168 msgb_put(msg, ret);
169
Harald Welte6c4136e2010-05-19 15:53:22 +0200170 if (msg->len < sizeof(*iph) + sizeof(*greh) + 2) {
171 LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
Harald Welteb3ee2652010-05-19 14:38:50 +0200172 *error = -EIO;
173 goto out_err;
174 }
175
Harald Welte6c4136e2010-05-19 15:53:22 +0200176 iph = (struct iphdr *) msg->data;
177 if (msg->len < (iph->ihl*4 + sizeof(*greh) + 2)) {
178 LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
179 *error = -EIO;
180 goto out_err;
181 }
182
183 greh = (struct gre_hdr *) (msg->data + iph->ihl*4);
Harald Welteb3ee2652010-05-19 14:38:50 +0200184 if (greh->flags) {
185 LOGP(DNS, LOGL_NOTICE, "Unknown GRE flags 0x%04x\n",
186 ntohs(greh->flags));
187 }
Harald Welte57a9cf22010-05-28 16:06:53 +0200188
189 switch (ntohs(greh->ptype)) {
190 case GRE_PTYPE_IPv4:
191 /* IPv4 messages might be GRE keepalives */
192 *error = handle_rx_gre_ipv4(bfd, msg, iph, greh);
193 goto out_err;
194 break;
195 case GRE_PTYPE_FR:
196 /* continue as usual */
197 break;
198 default:
Harald Welteb3ee2652010-05-19 14:38:50 +0200199 LOGP(DNS, LOGL_NOTICE, "Unknown GRE protocol 0x%04x != FR\n",
200 ntohs(greh->ptype));
201 *error = -EIO;
202 goto out_err;
Harald Welte57a9cf22010-05-28 16:06:53 +0200203 break;
Harald Welteb3ee2652010-05-19 14:38:50 +0200204 }
205
206 if (msg->len < sizeof(*greh) + 2) {
207 LOGP(DNS, LOGL_ERROR, "Short FR header: %u bytes\n", msg->len);
208 *error = -EIO;
209 goto out_err;
210 }
211
Harald Welte6c4136e2010-05-19 15:53:22 +0200212 frh = (uint8_t *)greh + sizeof(*greh);
Harald Welteb3ee2652010-05-19 14:38:50 +0200213 if (frh[0] & 0x01) {
214 LOGP(DNS, LOGL_NOTICE, "Unsupported single-byte FR address\n");
215 *error = -EIO;
216 goto out_err;
217 }
Harald Welte869aaa42010-05-19 16:48:12 +0200218 dlci = ((frh[0] & 0xfc) << 2);
Harald Welteb3ee2652010-05-19 14:38:50 +0200219 if ((frh[1] & 0x0f) != 0x01) {
220 LOGP(DNS, LOGL_NOTICE, "Unknown second FR octet 0x%02x\n",
221 frh[1]);
222 *error = -EIO;
223 goto out_err;
224 }
Harald Welte869aaa42010-05-19 16:48:12 +0200225 dlci |= (frh[1] >> 4);
Harald Welteb3ee2652010-05-19 14:38:50 +0200226
Harald Welte6c4136e2010-05-19 15:53:22 +0200227 msg->l2h = frh+2;
Harald Welteb3ee2652010-05-19 14:38:50 +0200228
229 /* Store DLCI in NETWORK BYTEORDER in sockaddr port member */
Harald Welte869aaa42010-05-19 16:48:12 +0200230 saddr->sin_port = htons(dlci);
Harald Welteb3ee2652010-05-19 14:38:50 +0200231
232 return msg;
233
234out_err:
235 msgb_free(msg);
236 return NULL;
237}
238
239int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
240 struct sockaddr_in *saddr, enum gprs_ns_ll ll);
241
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200242static int handle_nsfrgre_read(struct osmo_fd *bfd)
Harald Welteb3ee2652010-05-19 14:38:50 +0200243{
Harald Weltef15497c2010-05-28 16:12:57 +0200244 int rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200245 struct sockaddr_in saddr;
246 struct gprs_ns_inst *nsi = bfd->data;
Harald Weltef15497c2010-05-28 16:12:57 +0200247 struct msgb *msg;
248 uint16_t dlci;
Harald Welteb3ee2652010-05-19 14:38:50 +0200249
Harald Weltef15497c2010-05-28 16:12:57 +0200250 msg = read_nsfrgre_msg(bfd, &rc, &saddr);
Harald Welteb3ee2652010-05-19 14:38:50 +0200251 if (!msg)
Harald Weltef15497c2010-05-28 16:12:57 +0200252 return rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200253
Harald Weltef15497c2010-05-28 16:12:57 +0200254 dlci = ntohs(saddr.sin_port);
255 if (dlci == 0 || dlci == 1023) {
256 LOGP(DNS, LOGL_INFO, "Received FR on LMI DLCI %u - ignoring\n",
257 dlci);
258 rc = 0;
259 goto out;
260 }
Harald Welteb3ee2652010-05-19 14:38:50 +0200261
Harald Weltef15497c2010-05-28 16:12:57 +0200262 rc = gprs_ns_rcvmsg(nsi, msg, &saddr, GPRS_NS_LL_FR_GRE);
263out:
Harald Welteb3ee2652010-05-19 14:38:50 +0200264 msgb_free(msg);
265
Harald Weltef15497c2010-05-28 16:12:57 +0200266 return rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200267}
268
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200269static int handle_nsfrgre_write(struct osmo_fd *bfd)
Harald Welteb3ee2652010-05-19 14:38:50 +0200270{
271 /* FIXME: actually send the data here instead of nsip_sendmsg() */
272 return -EIO;
273}
274
275int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg)
276{
277 int rc;
278 struct gprs_ns_inst *nsi = nsvc->nsi;
279 struct sockaddr_in daddr;
280 uint16_t dlci = ntohs(nsvc->frgre.bts_addr.sin_port);
281 uint8_t *frh;
282 struct gre_hdr *greh;
283
284 /* Build socket address for the packet destionation */
Harald Welteac914b82010-05-19 15:37:34 +0200285 daddr.sin_family = AF_INET;
Harald Welteb3ee2652010-05-19 14:38:50 +0200286 daddr.sin_addr = nsvc->frgre.bts_addr.sin_addr;
287 daddr.sin_port = IPPROTO_GRE;
288
289 /* Prepend the FR header */
Harald Welteac914b82010-05-19 15:37:34 +0200290 frh = msgb_push(msg, 2);
Harald Welteb3ee2652010-05-19 14:38:50 +0200291 frh[0] = (dlci >> 2) & 0xfc;
Harald Welteac914b82010-05-19 15:37:34 +0200292 frh[1] = ((dlci & 0xf)<<4) | 0x01;
Harald Welteb3ee2652010-05-19 14:38:50 +0200293
294 /* Prepend the GRE header */
295 greh = (struct gre_hdr *) msgb_push(msg, sizeof(*greh));
296 greh->flags = 0;
Harald Welteac914b82010-05-19 15:37:34 +0200297 greh->ptype = htons(GRE_PTYPE_FR);
Harald Welteb3ee2652010-05-19 14:38:50 +0200298
299 rc = sendto(nsi->frgre.fd.fd, msg->data, msg->len, 0,
300 (struct sockaddr *)&daddr, sizeof(daddr));
301
Holger Hans Peter Freyther52746462012-03-01 20:30:32 +0100302 msgb_free(msg);
Harald Welteb3ee2652010-05-19 14:38:50 +0200303
304 return rc;
305}
306
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200307static int nsfrgre_fd_cb(struct osmo_fd *bfd, unsigned int what)
Harald Welteb3ee2652010-05-19 14:38:50 +0200308{
309 int rc = 0;
310
311 if (what & BSC_FD_READ)
312 rc = handle_nsfrgre_read(bfd);
313 if (what & BSC_FD_WRITE)
314 rc = handle_nsfrgre_write(bfd);
315
316 return rc;
317}
318
Harald Welte7fb05232010-05-19 15:09:09 +0200319int gprs_ns_frgre_listen(struct gprs_ns_inst *nsi)
Harald Welteb3ee2652010-05-19 14:38:50 +0200320{
Harald Welte73952e32012-06-16 14:59:56 +0800321 struct in_addr in;
Harald Welteb3ee2652010-05-19 14:38:50 +0200322 int rc;
323
Harald Welte73952e32012-06-16 14:59:56 +0800324 in.s_addr = htonl(nsi->frgre.local_ip);
325
Harald Welteb3ee2652010-05-19 14:38:50 +0200326 /* Make sure we close any existing socket before changing it */
327 if (nsi->frgre.fd.fd)
328 close(nsi->frgre.fd.fd);
329
Harald Welte7fb05232010-05-19 15:09:09 +0200330 if (!nsi->frgre.enabled)
331 return 0;
332
Harald Welte73952e32012-06-16 14:59:56 +0800333 nsi->frgre.fd.cb = nsfrgre_fd_cb;
334 nsi->frgre.fd.data = nsi;
335 rc = osmo_sock_init_ofd(&nsi->frgre.fd, AF_INET, SOCK_RAW,
336 IPPROTO_GRE, inet_ntoa(in), 0,
337 OSMO_SOCK_F_BIND);
Harald Welteb3ee2652010-05-19 14:38:50 +0200338 if (rc < 0) {
339 LOGP(DNS, LOGL_ERROR, "Error creating GRE socket (%s)\n",
340 strerror(errno));
341 return rc;
342 }
343 nsi->frgre.fd.data = nsi;
344
345 return rc;
346}