blob: be5f0f6c7d7a95e65d14524e86fa87271ad2a167 [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
Harald Welte57a9cf22010-05-28 16:06:53 +020051/* IPv4 messages inside the GRE tunnel might be GRE keepalives */
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +020052static int handle_rx_gre_ipv4(struct osmo_fd *bfd, struct msgb *msg,
Harald Welte57a9cf22010-05-28 16:06:53 +020053 struct iphdr *iph, struct gre_hdr *greh)
54{
55 struct gprs_ns_inst *nsi = bfd->data;
56 int gre_payload_len;
57 struct iphdr *inner_iph;
58 struct gre_hdr *inner_greh;
59 struct sockaddr_in daddr;
60 struct in_addr ia;
61
62 gre_payload_len = msg->len - (iph->ihl*4 + sizeof(*greh));
63
Harald Welte9681ce32010-05-31 11:02:57 +020064 inner_iph = (struct iphdr *) ((uint8_t *)greh + sizeof(*greh));
Harald Welte57a9cf22010-05-28 16:06:53 +020065
66 if (gre_payload_len < inner_iph->ihl*4 + sizeof(*inner_greh)) {
67 LOGP(DNS, LOGL_ERROR, "GRE keepalive too short\n");
68 return -EIO;
69 }
70
71 if (inner_iph->saddr != iph->daddr ||
72 inner_iph->daddr != iph->saddr) {
73 LOGP(DNS, LOGL_ERROR,
74 "GRE keepalive with wrong tunnel addresses\n");
75 return -EIO;
76 }
77
78 if (inner_iph->protocol != IPPROTO_GRE) {
79 LOGP(DNS, LOGL_ERROR, "GRE keepalive with wrong protocol\n");
80 return -EIO;
81 }
82
Harald Welte9681ce32010-05-31 11:02:57 +020083 inner_greh = (struct gre_hdr *) ((uint8_t *)inner_iph + iph->ihl*4);
Harald Welte57a9cf22010-05-28 16:06:53 +020084 if (inner_greh->ptype != htons(GRE_PTYPE_KAR)) {
85 LOGP(DNS, LOGL_ERROR, "GRE keepalive inner GRE type != 0\n");
86 return -EIO;
87 }
88
89 /* Actually send the response back */
90
91 daddr.sin_family = AF_INET;
92 daddr.sin_addr.s_addr = inner_iph->daddr;
93 daddr.sin_port = IPPROTO_GRE;
94
95 ia.s_addr = iph->saddr;
96 LOGP(DNS, LOGL_DEBUG, "GRE keepalive from %s, responding\n",
97 inet_ntoa(ia));
98
99 return sendto(nsi->frgre.fd.fd, inner_greh,
100 gre_payload_len - inner_iph->ihl*4, 0,
101 (struct sockaddr *)&daddr, sizeof(daddr));
102}
103
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200104static struct msgb *read_nsfrgre_msg(struct osmo_fd *bfd, int *error,
Harald Welteb3ee2652010-05-19 14:38:50 +0200105 struct sockaddr_in *saddr)
106{
107 struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
108 int ret = 0;
109 socklen_t saddr_len = sizeof(*saddr);
Harald Welte6c4136e2010-05-19 15:53:22 +0200110 struct iphdr *iph;
Harald Welteb3ee2652010-05-19 14:38:50 +0200111 struct gre_hdr *greh;
112 uint8_t *frh;
Harald Welte869aaa42010-05-19 16:48:12 +0200113 uint16_t dlci;
Harald Welteb3ee2652010-05-19 14:38:50 +0200114
115 if (!msg) {
116 *error = -ENOMEM;
117 return NULL;
118 }
119
120 ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0,
121 (struct sockaddr *)saddr, &saddr_len);
122 if (ret < 0) {
123 LOGP(DNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n",
124 strerror(errno));
125 *error = ret;
Harald Welteb11226d2010-05-19 15:46:49 +0200126 goto out_err;
Harald Welteb3ee2652010-05-19 14:38:50 +0200127 } else if (ret == 0) {
Harald Welteb3ee2652010-05-19 14:38:50 +0200128 *error = ret;
Harald Welteb11226d2010-05-19 15:46:49 +0200129 goto out_err;
Harald Welteb3ee2652010-05-19 14:38:50 +0200130 }
131
132 msgb_put(msg, ret);
133
Harald Welte6c4136e2010-05-19 15:53:22 +0200134 if (msg->len < sizeof(*iph) + sizeof(*greh) + 2) {
135 LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
Harald Welteb3ee2652010-05-19 14:38:50 +0200136 *error = -EIO;
137 goto out_err;
138 }
139
Harald Welte6c4136e2010-05-19 15:53:22 +0200140 iph = (struct iphdr *) msg->data;
141 if (msg->len < (iph->ihl*4 + sizeof(*greh) + 2)) {
142 LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
143 *error = -EIO;
144 goto out_err;
145 }
146
147 greh = (struct gre_hdr *) (msg->data + iph->ihl*4);
Harald Welteb3ee2652010-05-19 14:38:50 +0200148 if (greh->flags) {
149 LOGP(DNS, LOGL_NOTICE, "Unknown GRE flags 0x%04x\n",
150 ntohs(greh->flags));
151 }
Harald Welte57a9cf22010-05-28 16:06:53 +0200152
153 switch (ntohs(greh->ptype)) {
154 case GRE_PTYPE_IPv4:
155 /* IPv4 messages might be GRE keepalives */
156 *error = handle_rx_gre_ipv4(bfd, msg, iph, greh);
157 goto out_err;
158 break;
159 case GRE_PTYPE_FR:
160 /* continue as usual */
161 break;
162 default:
Harald Welteb3ee2652010-05-19 14:38:50 +0200163 LOGP(DNS, LOGL_NOTICE, "Unknown GRE protocol 0x%04x != FR\n",
164 ntohs(greh->ptype));
165 *error = -EIO;
166 goto out_err;
Harald Welte57a9cf22010-05-28 16:06:53 +0200167 break;
Harald Welteb3ee2652010-05-19 14:38:50 +0200168 }
169
170 if (msg->len < sizeof(*greh) + 2) {
171 LOGP(DNS, LOGL_ERROR, "Short FR header: %u bytes\n", msg->len);
172 *error = -EIO;
173 goto out_err;
174 }
175
Harald Welte6c4136e2010-05-19 15:53:22 +0200176 frh = (uint8_t *)greh + sizeof(*greh);
Harald Welteb3ee2652010-05-19 14:38:50 +0200177 if (frh[0] & 0x01) {
178 LOGP(DNS, LOGL_NOTICE, "Unsupported single-byte FR address\n");
179 *error = -EIO;
180 goto out_err;
181 }
Harald Welte869aaa42010-05-19 16:48:12 +0200182 dlci = ((frh[0] & 0xfc) << 2);
Harald Welteb3ee2652010-05-19 14:38:50 +0200183 if ((frh[1] & 0x0f) != 0x01) {
184 LOGP(DNS, LOGL_NOTICE, "Unknown second FR octet 0x%02x\n",
185 frh[1]);
186 *error = -EIO;
187 goto out_err;
188 }
Harald Welte869aaa42010-05-19 16:48:12 +0200189 dlci |= (frh[1] >> 4);
Harald Welteb3ee2652010-05-19 14:38:50 +0200190
Harald Welte6c4136e2010-05-19 15:53:22 +0200191 msg->l2h = frh+2;
Harald Welteb3ee2652010-05-19 14:38:50 +0200192
193 /* Store DLCI in NETWORK BYTEORDER in sockaddr port member */
Harald Welte869aaa42010-05-19 16:48:12 +0200194 saddr->sin_port = htons(dlci);
Harald Welteb3ee2652010-05-19 14:38:50 +0200195
196 return msg;
197
198out_err:
199 msgb_free(msg);
200 return NULL;
201}
202
203int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
204 struct sockaddr_in *saddr, enum gprs_ns_ll ll);
205
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200206static int handle_nsfrgre_read(struct osmo_fd *bfd)
Harald Welteb3ee2652010-05-19 14:38:50 +0200207{
Harald Weltef15497c2010-05-28 16:12:57 +0200208 int rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200209 struct sockaddr_in saddr;
210 struct gprs_ns_inst *nsi = bfd->data;
Harald Weltef15497c2010-05-28 16:12:57 +0200211 struct msgb *msg;
212 uint16_t dlci;
Harald Welteb3ee2652010-05-19 14:38:50 +0200213
Harald Weltef15497c2010-05-28 16:12:57 +0200214 msg = read_nsfrgre_msg(bfd, &rc, &saddr);
Harald Welteb3ee2652010-05-19 14:38:50 +0200215 if (!msg)
Harald Weltef15497c2010-05-28 16:12:57 +0200216 return rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200217
Harald Weltef15497c2010-05-28 16:12:57 +0200218 dlci = ntohs(saddr.sin_port);
219 if (dlci == 0 || dlci == 1023) {
220 LOGP(DNS, LOGL_INFO, "Received FR on LMI DLCI %u - ignoring\n",
221 dlci);
222 rc = 0;
223 goto out;
224 }
Harald Welteb3ee2652010-05-19 14:38:50 +0200225
Harald Weltef15497c2010-05-28 16:12:57 +0200226 rc = gprs_ns_rcvmsg(nsi, msg, &saddr, GPRS_NS_LL_FR_GRE);
227out:
Harald Welteb3ee2652010-05-19 14:38:50 +0200228 msgb_free(msg);
229
Harald Weltef15497c2010-05-28 16:12:57 +0200230 return rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200231}
232
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200233static int handle_nsfrgre_write(struct osmo_fd *bfd)
Harald Welteb3ee2652010-05-19 14:38:50 +0200234{
235 /* FIXME: actually send the data here instead of nsip_sendmsg() */
236 return -EIO;
237}
238
239int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg)
240{
241 int rc;
242 struct gprs_ns_inst *nsi = nsvc->nsi;
243 struct sockaddr_in daddr;
244 uint16_t dlci = ntohs(nsvc->frgre.bts_addr.sin_port);
245 uint8_t *frh;
246 struct gre_hdr *greh;
247
248 /* Build socket address for the packet destionation */
Harald Welteac914b82010-05-19 15:37:34 +0200249 daddr.sin_family = AF_INET;
Harald Welteb3ee2652010-05-19 14:38:50 +0200250 daddr.sin_addr = nsvc->frgre.bts_addr.sin_addr;
251 daddr.sin_port = IPPROTO_GRE;
252
253 /* Prepend the FR header */
Harald Welteac914b82010-05-19 15:37:34 +0200254 frh = msgb_push(msg, 2);
Harald Welteb3ee2652010-05-19 14:38:50 +0200255 frh[0] = (dlci >> 2) & 0xfc;
Harald Welteac914b82010-05-19 15:37:34 +0200256 frh[1] = ((dlci & 0xf)<<4) | 0x01;
Harald Welteb3ee2652010-05-19 14:38:50 +0200257
258 /* Prepend the GRE header */
259 greh = (struct gre_hdr *) msgb_push(msg, sizeof(*greh));
260 greh->flags = 0;
Harald Welteac914b82010-05-19 15:37:34 +0200261 greh->ptype = htons(GRE_PTYPE_FR);
Harald Welteb3ee2652010-05-19 14:38:50 +0200262
263 rc = sendto(nsi->frgre.fd.fd, msg->data, msg->len, 0,
264 (struct sockaddr *)&daddr, sizeof(daddr));
265
Holger Hans Peter Freyther52746462012-03-01 20:30:32 +0100266 msgb_free(msg);
Harald Welteb3ee2652010-05-19 14:38:50 +0200267
268 return rc;
269}
270
Pablo Neira Ayusobfadfb72011-05-06 12:11:23 +0200271static int nsfrgre_fd_cb(struct osmo_fd *bfd, unsigned int what)
Harald Welteb3ee2652010-05-19 14:38:50 +0200272{
273 int rc = 0;
274
275 if (what & BSC_FD_READ)
276 rc = handle_nsfrgre_read(bfd);
277 if (what & BSC_FD_WRITE)
278 rc = handle_nsfrgre_write(bfd);
279
280 return rc;
281}
282
Harald Welte7fb05232010-05-19 15:09:09 +0200283int gprs_ns_frgre_listen(struct gprs_ns_inst *nsi)
Harald Welteb3ee2652010-05-19 14:38:50 +0200284{
Harald Welte73952e32012-06-16 14:59:56 +0800285 struct in_addr in;
Harald Welteb3ee2652010-05-19 14:38:50 +0200286 int rc;
287
Harald Welte73952e32012-06-16 14:59:56 +0800288 in.s_addr = htonl(nsi->frgre.local_ip);
289
Harald Welteb3ee2652010-05-19 14:38:50 +0200290 /* Make sure we close any existing socket before changing it */
291 if (nsi->frgre.fd.fd)
292 close(nsi->frgre.fd.fd);
293
Harald Welte7fb05232010-05-19 15:09:09 +0200294 if (!nsi->frgre.enabled)
295 return 0;
296
Harald Welte73952e32012-06-16 14:59:56 +0800297 nsi->frgre.fd.cb = nsfrgre_fd_cb;
298 nsi->frgre.fd.data = nsi;
299 rc = osmo_sock_init_ofd(&nsi->frgre.fd, AF_INET, SOCK_RAW,
300 IPPROTO_GRE, inet_ntoa(in), 0,
301 OSMO_SOCK_F_BIND);
Harald Welteb3ee2652010-05-19 14:38:50 +0200302 if (rc < 0) {
303 LOGP(DNS, LOGL_ERROR, "Error creating GRE socket (%s)\n",
304 strerror(errno));
305 return rc;
306 }
307 nsi->frgre.fd.data = nsi;
308
309 return rc;
310}