blob: 7436d0dcdd3b77f1ca85f3dd603902fef7c09110 [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
11 * 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
13 * (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
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 *
24 */
25
26#include <errno.h>
27#include <string.h>
28#include <unistd.h>
29
30#include <sys/socket.h>
31#include <netinet/in.h>
Harald Welte6c4136e2010-05-19 15:53:22 +020032#include <netinet/ip.h>
Harald Welteb3ee2652010-05-19 14:38:50 +020033#include <arpa/inet.h>
34
35#include <osmocore/select.h>
36#include <osmocore/msgb.h>
37#include <osmocore/talloc.h>
38
39#include <openbsc/socket.h>
40#include <openbsc/debug.h>
41#include <openbsc/gprs_ns.h>
42
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
47struct gre_hdr {
48 uint16_t flags;
49 uint16_t ptype;
50} __attribute__ ((packed));
51
Harald Welte57a9cf22010-05-28 16:06:53 +020052/* IPv4 messages inside the GRE tunnel might be GRE keepalives */
53static int handle_rx_gre_ipv4(struct bsc_fd *bfd, struct msgb *msg,
54 struct iphdr *iph, struct gre_hdr *greh)
55{
56 struct gprs_ns_inst *nsi = bfd->data;
57 int gre_payload_len;
58 struct iphdr *inner_iph;
59 struct gre_hdr *inner_greh;
60 struct sockaddr_in daddr;
61 struct in_addr ia;
62
63 gre_payload_len = msg->len - (iph->ihl*4 + sizeof(*greh));
64
Harald Welte9681ce32010-05-31 11:02:57 +020065 inner_iph = (struct iphdr *) ((uint8_t *)greh + sizeof(*greh));
Harald Welte57a9cf22010-05-28 16:06:53 +020066
67 if (gre_payload_len < inner_iph->ihl*4 + sizeof(*inner_greh)) {
68 LOGP(DNS, LOGL_ERROR, "GRE keepalive too short\n");
69 return -EIO;
70 }
71
72 if (inner_iph->saddr != iph->daddr ||
73 inner_iph->daddr != iph->saddr) {
74 LOGP(DNS, LOGL_ERROR,
75 "GRE keepalive with wrong tunnel addresses\n");
76 return -EIO;
77 }
78
79 if (inner_iph->protocol != IPPROTO_GRE) {
80 LOGP(DNS, LOGL_ERROR, "GRE keepalive with wrong protocol\n");
81 return -EIO;
82 }
83
Harald Welte9681ce32010-05-31 11:02:57 +020084 inner_greh = (struct gre_hdr *) ((uint8_t *)inner_iph + iph->ihl*4);
Harald Welte57a9cf22010-05-28 16:06:53 +020085 if (inner_greh->ptype != htons(GRE_PTYPE_KAR)) {
86 LOGP(DNS, LOGL_ERROR, "GRE keepalive inner GRE type != 0\n");
87 return -EIO;
88 }
89
90 /* Actually send the response back */
91
92 daddr.sin_family = AF_INET;
93 daddr.sin_addr.s_addr = inner_iph->daddr;
94 daddr.sin_port = IPPROTO_GRE;
95
96 ia.s_addr = iph->saddr;
97 LOGP(DNS, LOGL_DEBUG, "GRE keepalive from %s, responding\n",
98 inet_ntoa(ia));
99
100 return sendto(nsi->frgre.fd.fd, inner_greh,
101 gre_payload_len - inner_iph->ihl*4, 0,
102 (struct sockaddr *)&daddr, sizeof(daddr));
103}
104
Harald Welteb3ee2652010-05-19 14:38:50 +0200105static struct msgb *read_nsfrgre_msg(struct bsc_fd *bfd, int *error,
106 struct sockaddr_in *saddr)
107{
108 struct msgb *msg = msgb_alloc(NS_ALLOC_SIZE, "Gb/NS/FR/GRE Rx");
109 int ret = 0;
110 socklen_t saddr_len = sizeof(*saddr);
Harald Welte6c4136e2010-05-19 15:53:22 +0200111 struct iphdr *iph;
Harald Welteb3ee2652010-05-19 14:38:50 +0200112 struct gre_hdr *greh;
113 uint8_t *frh;
Harald Welte869aaa42010-05-19 16:48:12 +0200114 uint16_t dlci;
Harald Welteb3ee2652010-05-19 14:38:50 +0200115
116 if (!msg) {
117 *error = -ENOMEM;
118 return NULL;
119 }
120
121 ret = recvfrom(bfd->fd, msg->data, NS_ALLOC_SIZE, 0,
122 (struct sockaddr *)saddr, &saddr_len);
123 if (ret < 0) {
124 LOGP(DNS, LOGL_ERROR, "recv error %s during NS-FR-GRE recv\n",
125 strerror(errno));
126 *error = ret;
Harald Welteb11226d2010-05-19 15:46:49 +0200127 goto out_err;
Harald Welteb3ee2652010-05-19 14:38:50 +0200128 } else if (ret == 0) {
Harald Welteb3ee2652010-05-19 14:38:50 +0200129 *error = ret;
Harald Welteb11226d2010-05-19 15:46:49 +0200130 goto out_err;
Harald Welteb3ee2652010-05-19 14:38:50 +0200131 }
132
133 msgb_put(msg, ret);
134
Harald Welte6c4136e2010-05-19 15:53:22 +0200135 if (msg->len < sizeof(*iph) + sizeof(*greh) + 2) {
136 LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
Harald Welteb3ee2652010-05-19 14:38:50 +0200137 *error = -EIO;
138 goto out_err;
139 }
140
Harald Welte6c4136e2010-05-19 15:53:22 +0200141 iph = (struct iphdr *) msg->data;
142 if (msg->len < (iph->ihl*4 + sizeof(*greh) + 2)) {
143 LOGP(DNS, LOGL_ERROR, "Short IP packet: %u bytes\n", msg->len);
144 *error = -EIO;
145 goto out_err;
146 }
147
148 greh = (struct gre_hdr *) (msg->data + iph->ihl*4);
Harald Welteb3ee2652010-05-19 14:38:50 +0200149 if (greh->flags) {
150 LOGP(DNS, LOGL_NOTICE, "Unknown GRE flags 0x%04x\n",
151 ntohs(greh->flags));
152 }
Harald Welte57a9cf22010-05-28 16:06:53 +0200153
154 switch (ntohs(greh->ptype)) {
155 case GRE_PTYPE_IPv4:
156 /* IPv4 messages might be GRE keepalives */
157 *error = handle_rx_gre_ipv4(bfd, msg, iph, greh);
158 goto out_err;
159 break;
160 case GRE_PTYPE_FR:
161 /* continue as usual */
162 break;
163 default:
Harald Welteb3ee2652010-05-19 14:38:50 +0200164 LOGP(DNS, LOGL_NOTICE, "Unknown GRE protocol 0x%04x != FR\n",
165 ntohs(greh->ptype));
166 *error = -EIO;
167 goto out_err;
Harald Welte57a9cf22010-05-28 16:06:53 +0200168 break;
Harald Welteb3ee2652010-05-19 14:38:50 +0200169 }
170
171 if (msg->len < sizeof(*greh) + 2) {
172 LOGP(DNS, LOGL_ERROR, "Short FR header: %u bytes\n", msg->len);
173 *error = -EIO;
174 goto out_err;
175 }
176
Harald Welte6c4136e2010-05-19 15:53:22 +0200177 frh = (uint8_t *)greh + sizeof(*greh);
Harald Welteb3ee2652010-05-19 14:38:50 +0200178 if (frh[0] & 0x01) {
179 LOGP(DNS, LOGL_NOTICE, "Unsupported single-byte FR address\n");
180 *error = -EIO;
181 goto out_err;
182 }
Harald Welte869aaa42010-05-19 16:48:12 +0200183 dlci = ((frh[0] & 0xfc) << 2);
Harald Welteb3ee2652010-05-19 14:38:50 +0200184 if ((frh[1] & 0x0f) != 0x01) {
185 LOGP(DNS, LOGL_NOTICE, "Unknown second FR octet 0x%02x\n",
186 frh[1]);
187 *error = -EIO;
188 goto out_err;
189 }
Harald Welte869aaa42010-05-19 16:48:12 +0200190 dlci |= (frh[1] >> 4);
Harald Welteb3ee2652010-05-19 14:38:50 +0200191
Harald Welte6c4136e2010-05-19 15:53:22 +0200192 msg->l2h = frh+2;
Harald Welteb3ee2652010-05-19 14:38:50 +0200193
194 /* Store DLCI in NETWORK BYTEORDER in sockaddr port member */
Harald Welte869aaa42010-05-19 16:48:12 +0200195 saddr->sin_port = htons(dlci);
Harald Welteb3ee2652010-05-19 14:38:50 +0200196
197 return msg;
198
199out_err:
200 msgb_free(msg);
201 return NULL;
202}
203
204int gprs_ns_rcvmsg(struct gprs_ns_inst *nsi, struct msgb *msg,
205 struct sockaddr_in *saddr, enum gprs_ns_ll ll);
206
207static int handle_nsfrgre_read(struct bsc_fd *bfd)
208{
Harald Weltef15497c2010-05-28 16:12:57 +0200209 int rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200210 struct sockaddr_in saddr;
211 struct gprs_ns_inst *nsi = bfd->data;
Harald Weltef15497c2010-05-28 16:12:57 +0200212 struct msgb *msg;
213 uint16_t dlci;
Harald Welteb3ee2652010-05-19 14:38:50 +0200214
Harald Weltef15497c2010-05-28 16:12:57 +0200215 msg = read_nsfrgre_msg(bfd, &rc, &saddr);
Harald Welteb3ee2652010-05-19 14:38:50 +0200216 if (!msg)
Harald Weltef15497c2010-05-28 16:12:57 +0200217 return rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200218
Harald Weltef15497c2010-05-28 16:12:57 +0200219 dlci = ntohs(saddr.sin_port);
220 if (dlci == 0 || dlci == 1023) {
221 LOGP(DNS, LOGL_INFO, "Received FR on LMI DLCI %u - ignoring\n",
222 dlci);
223 rc = 0;
224 goto out;
225 }
Harald Welteb3ee2652010-05-19 14:38:50 +0200226
Harald Weltef15497c2010-05-28 16:12:57 +0200227 rc = gprs_ns_rcvmsg(nsi, msg, &saddr, GPRS_NS_LL_FR_GRE);
228out:
Harald Welteb3ee2652010-05-19 14:38:50 +0200229 msgb_free(msg);
230
Harald Weltef15497c2010-05-28 16:12:57 +0200231 return rc;
Harald Welteb3ee2652010-05-19 14:38:50 +0200232}
233
234static int handle_nsfrgre_write(struct bsc_fd *bfd)
235{
236 /* FIXME: actually send the data here instead of nsip_sendmsg() */
237 return -EIO;
238}
239
240int gprs_ns_frgre_sendmsg(struct gprs_nsvc *nsvc, struct msgb *msg)
241{
242 int rc;
243 struct gprs_ns_inst *nsi = nsvc->nsi;
244 struct sockaddr_in daddr;
245 uint16_t dlci = ntohs(nsvc->frgre.bts_addr.sin_port);
246 uint8_t *frh;
247 struct gre_hdr *greh;
248
249 /* Build socket address for the packet destionation */
Harald Welteac914b82010-05-19 15:37:34 +0200250 daddr.sin_family = AF_INET;
Harald Welteb3ee2652010-05-19 14:38:50 +0200251 daddr.sin_addr = nsvc->frgre.bts_addr.sin_addr;
252 daddr.sin_port = IPPROTO_GRE;
253
254 /* Prepend the FR header */
Harald Welteac914b82010-05-19 15:37:34 +0200255 frh = msgb_push(msg, 2);
Harald Welteb3ee2652010-05-19 14:38:50 +0200256 frh[0] = (dlci >> 2) & 0xfc;
Harald Welteac914b82010-05-19 15:37:34 +0200257 frh[1] = ((dlci & 0xf)<<4) | 0x01;
Harald Welteb3ee2652010-05-19 14:38:50 +0200258
259 /* Prepend the GRE header */
260 greh = (struct gre_hdr *) msgb_push(msg, sizeof(*greh));
261 greh->flags = 0;
Harald Welteac914b82010-05-19 15:37:34 +0200262 greh->ptype = htons(GRE_PTYPE_FR);
Harald Welteb3ee2652010-05-19 14:38:50 +0200263
264 rc = sendto(nsi->frgre.fd.fd, msg->data, msg->len, 0,
265 (struct sockaddr *)&daddr, sizeof(daddr));
266
267 talloc_free(msg);
268
269 return rc;
270}
271
272static int nsfrgre_fd_cb(struct bsc_fd *bfd, unsigned int what)
273{
274 int rc = 0;
275
276 if (what & BSC_FD_READ)
277 rc = handle_nsfrgre_read(bfd);
278 if (what & BSC_FD_WRITE)
279 rc = handle_nsfrgre_write(bfd);
280
281 return rc;
282}
283
Harald Welte7fb05232010-05-19 15:09:09 +0200284int gprs_ns_frgre_listen(struct gprs_ns_inst *nsi)
Harald Welteb3ee2652010-05-19 14:38:50 +0200285{
286 int rc;
287
288 /* Make sure we close any existing socket before changing it */
289 if (nsi->frgre.fd.fd)
290 close(nsi->frgre.fd.fd);
291
Harald Welte7fb05232010-05-19 15:09:09 +0200292 if (!nsi->frgre.enabled)
293 return 0;
294
295 rc = make_sock(&nsi->frgre.fd, IPPROTO_GRE, nsi->frgre.local_ip,
296 0, nsfrgre_fd_cb);
Harald Welteb3ee2652010-05-19 14:38:50 +0200297 if (rc < 0) {
298 LOGP(DNS, LOGL_ERROR, "Error creating GRE socket (%s)\n",
299 strerror(errno));
300 return rc;
301 }
302 nsi->frgre.fd.data = nsi;
303
304 return rc;
305}