aboutsummaryrefslogtreecommitdiffstats
path: root/gtp/gtp.c
diff options
context:
space:
mode:
authorjjako <jjako>2003-10-13 20:33:30 +0000
committerjjako <jjako>2003-10-13 20:33:30 +0000
commit08d331db63cf42d16d0b2b00533a37fde79eb2c2 (patch)
treef21b884722d5e3a1f7bc5c9ea261a6a04fdaf822 /gtp/gtp.c
parent48c205f3f02161cc86d97cac92577f19eda2ba34 (diff)
First attempt at a GTP1 implementation
Diffstat (limited to 'gtp/gtp.c')
-rw-r--r--gtp/gtp.c2071
1 files changed, 1384 insertions, 687 deletions
diff --git a/gtp/gtp.c b/gtp/gtp.c
index 7d36dbb..27d8a03 100644
--- a/gtp/gtp.c
+++ b/gtp/gtp.c
@@ -122,171 +122,162 @@ int gtp_freepdp(struct gsn_t* gsn, struct pdp_t *pdp) {
return pdp_freepdp(pdp);
}
-int gtp_create_context(struct gsn_t *gsn, struct pdp_t *pdp, void *aid,
- struct in_addr* inetaddr) {
- int version = 0;
-
- return gtp_create_pdp_req(gsn, version, aid, inetaddr, pdp);
-}
-
-int gtp_create_context2(struct gsn_t *gsn, void *aid,
- struct in_addr* inetaddr,
- int selmode, uint64_t imsi, int nsapi,
- uint8_t *qos, int qoslen,
- char *apn, int apnlen,
- char *msisdn, int msisdnlen,
- uint8_t *pco, int pcolen) {
- int version = 0;
-
- struct pdp_t *pdp;
-
- if (qoslen > sizeof(pdp->qos_req0)) {
- gtp_err(LOG_ERR, __FILE__, __LINE__, 0, "QoS length too big");
- return -1;
- }
-
- if (apnlen > sizeof(pdp->apn_use.v)) {
- gtp_err(LOG_ERR, __FILE__, __LINE__, 0, "APN length too big");
- return -1;
- }
-
- if (msisdnlen > sizeof(pdp->msisdn.v)) {
- gtp_err(LOG_ERR, __FILE__, __LINE__, 0, "MSISDN length too big");
- return -1;
- }
-
- if (pcolen > sizeof(pdp->pco_req.v)) {
- gtp_err(LOG_ERR, __FILE__, __LINE__, 0, "PCO length too big");
- return -1;
- }
-
- /* New pdp allocated here:*/
- pdp_newpdp(&pdp, imsi, nsapi, NULL);
-
- pdp->peer = aid;
- pdp->ipif = NULL;
-
- pdp->selmode = selmode;
-
- memcpy(pdp->qos_req0, qos, qoslen); /* Length checked above */
- pdp->apn_use.l = apnlen;
- memcpy(pdp->apn_use.v, apn, apnlen); /* Length checked above */
-
- pdp->gsnlc.l = sizeof(gsn->gsnc);
- memcpy(pdp->gsnlc.v, &gsn->gsnc, sizeof(gsn->gsnc));
- pdp->gsnlu.l = sizeof(gsn->gsnc);
- memcpy(pdp->gsnlu.v, &gsn->gsnc, sizeof(gsn->gsnc));
-
- pdp->msisdn.l = msisdnlen;
- memcpy(pdp->msisdn.v, msisdn, msisdnlen);
-
- ipv42eua(&pdp->eua, NULL); /* Request dynamic IP address */
-
- pdp->pco_req.l = pcolen;
- memcpy(pdp->pco_req.v, pco, pcolen);
-
- return gtp_create_pdp_req(gsn, version, aid, inetaddr, pdp);
-}
-
-int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *aid,
- struct in_addr* inetaddr) {
- int version = 0;
-
- return gtp_update_pdp_req(gsn, version, aid, inetaddr, pdp);
-}
-
-int gtp_delete_context(struct gsn_t *gsn, struct pdp_t *pdp, void *aid) {
- int version = 0;
- return gtp_delete_pdp_req(gsn, version, aid, pdp);
-}
-
/* gtp_gpdu */
extern int gtp_fd(struct gsn_t *gsn) {
- return gsn->fd;
+ return gsn->fd0;
}
/* gtp_decaps */
/* gtp_retrans */
/* gtp_retranstimeout */
-int gtp_set_cb_delete_context(struct gsn_t *gsn,
- int (*cb_delete_context) (struct pdp_t* pdp))
-{
- gsn->cb_delete_context = cb_delete_context;
- return 0;
-}
-int gtp_set_cb_create_context(struct gsn_t *gsn,
- int (*cb_create_context) (struct pdp_t* pdp))
-{
- gsn->cb_create_context = cb_create_context;
+int gtp_set_cb_unsup_ind(struct gsn_t *gsn,
+ int (*cb) (struct sockaddr_in *peer)) {
+ gsn->cb_unsup_ind = cb;
return 0;
}
-/*
- int gtp_set_cb_create_pdp_conf(struct gsn_t *gsn,
- int (*cb) (struct pdp_t*, int))
- {
- gsn->cb_create_pdp_conf = cb;
+/* API: Initialise delete context callback */
+/* Called whenever a pdp context is deleted for any reason */
+int gtp_set_cb_delete_context(struct gsn_t *gsn,
+ int (*cb) (struct pdp_t* pdp))
+{
+ gsn->cb_delete_context = cb;
return 0;
- }
-
- int gtp_set_cb_update_pdp_conf(struct gsn_t *gsn,
- int (*cb) (struct pdp_t*, int, int))
- {
- gsn->cb_update_pdp_conf = cb;
- return 0;
-}
-
-in t gtp_set_cb_delete_pdp_conf(struct gsn_t *gsn,
-int (*cb) (struct pdp_t*, int))
- {
-gsn->cb_delete_pdp_conf = cb;
-return 0;
}
-*/
-
int gtp_set_cb_conf(struct gsn_t *gsn,
int (*cb) (int type, int cause,
- struct pdp_t* pdp, void *aid)) {
+ struct pdp_t* pdp, void *cbp)) {
gsn->cb_conf = cb;
return 0;
}
-extern int gtp_set_cb_gpdu(struct gsn_t *gsn,
- int (*cb_gpdu) (struct pdp_t* pdp,
+extern int gtp_set_cb_data_ind(struct gsn_t *gsn,
+ int (*cb_data_ind) (struct pdp_t* pdp,
void* pack,
unsigned len))
{
- gsn->cb_gpdu = cb_gpdu;
+ gsn->cb_data_ind = cb_data_ind;
return 0;
}
-
-void get_default_gtp(int version, void *packet) {
+/**
+ * get_default_gtp()
+ * Generate a GPRS Tunneling Protocol signalling packet header, depending
+ * on GTP version and message type. pdp is used for teid/flow label.
+ * *packet must be allocated by the calling function, and be large enough
+ * to hold the packet header.
+ * returns the length of the header. 0 on error.
+ **/
+static int get_default_gtp(int version, u_int8_t type, void *packet) {
struct gtp0_header *gtp0_default = (struct gtp0_header*) packet;
struct gtp1_header_long *gtp1_default = (struct gtp1_header_long*) packet;
switch (version) {
case 0:
/* Initialise "standard" GTP0 header */
- memset(gtp0_default, 0, sizeof(gtp0_default));
+ memset(gtp0_default, 0, sizeof(struct gtp0_header));
gtp0_default->flags=0x1e;
+ gtp0_default->type=hton8(type);
gtp0_default->spare1=0xff;
gtp0_default->spare2=0xff;
gtp0_default->spare3=0xff;
gtp0_default->number=0xff;
- break;
+ return GTP0_HEADER_SIZE;
case 1:
/* Initialise "standard" GTP1 header */
- memset(gtp1_default, 0, sizeof(gtp1_default));
- gtp0_default->flags=0x1e;
- break;
+ /* 29.060: 8.2: S=1 and PN=0 */
+ /* 29.060 9.3.1: For GTP-U messages Echo Request, Echo Response */
+ /* and Supported Extension Headers Notification, the S field shall be */
+ /* set to 1 */
+ /* Currently extension headers are not supported */
+ memset(gtp1_default, 0, sizeof(struct gtp1_header_long));
+ gtp1_default->flags=0x32; /* No extension, enable sequence, no N-PDU */
+ gtp1_default->type=hton8(type);
+ return GTP1_HEADER_SIZE_LONG;
+ default:
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "Unknown GTP packet version");
+ return 0;
+ }
+}
+
+/**
+ * get_seq()
+ * Get sequence number of a packet.
+ * Returns 0 on error
+ **/
+static uint16_t get_seq(void *pack) {
+ union gtp_packet *packet = (union gtp_packet *) pack;
+
+ if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
+ return ntoh16(packet->gtp0.h.seq);
+ }
+ else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
+ return ntoh16(packet->gtp1l.h.seq);
+ } else {
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "Unknown packet flag");
+ return 0;
+ }
+}
+
+/**
+ * get_tid()
+ * Get tunnel identifier of a packet.
+ * Returns 0 on error
+ **/
+static uint64_t get_tid(void *pack) {
+ union gtp_packet *packet = (union gtp_packet *) pack;
+
+ if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
+ return packet->gtp0.h.tid;
+ }
+ return 0;
+}
+
+/**
+ * get_hlen()
+ * Get the header length of a packet.
+ * Returns 0 on error
+ **/
+static uint16_t get_hlen(void *pack) {
+ union gtp_packet *packet = (union gtp_packet *) pack;
+
+ if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
+ return GTP0_HEADER_SIZE;
+ }
+ else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
+ return GTP1_HEADER_SIZE_LONG;
+ }
+ else if ((packet->flags & 0xe7) == 0x20) { /* Short version 1 */
+ return GTP1_HEADER_SIZE_SHORT;
+ } else {
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "Unknown packet flag");
+ return 0;
}
}
+/**
+ * get_tei()
+ * Get the tunnel endpoint identifier (flow label) of a packet.
+ * Returns 0xffffffff on error.
+ **/
+static uint32_t get_tei(void *pack) {
+ union gtp_packet *packet = (union gtp_packet *) pack;
+
+ if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
+ return ntoh16(packet->gtp0.h.flow);
+ }
+ else if ((packet->flags & 0xe0) == 0x20) { /* Version 1 */
+ return ntoh32(packet->gtp1l.h.tei);
+ }
+ else {
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "Unknown packet flag");
+ return 0xffffffff;
+ }
+}
int print_packet(void *packet, unsigned len)
@@ -362,8 +353,8 @@ char* snprint_packet(struct gsn_t *gsn, struct sockaddr_in *peer,
* Create pdp context: The SGSN may send create context request even if
* a context allready exist (imsi+nsapi?). This means that the reply will
automatically dublicate the original response. It might however have
- * sideeffects in the application which is asked twice to allocate
- * validate the login.
+ * side effects in the application which is asked twice to validate
+ * the login.
* Update pdp context: Automatically dublicates the original response???
* Delete pdp context. Automatically in gtp0, but in gtp1 will generate
* a nonexist reply message.
@@ -390,28 +381,57 @@ char* snprint_packet(struct gsn_t *gsn, struct sockaddr_in *peer,
* gtp_conf:
* Remove an incoming confirmation from the queue
* gtp_resp:
- * Send off a responce to a request. Use the same sequence
+ * Send off a response to a request. Use the same sequence
* number in the response as in the request.
* gtp_retrans:
* Retransmit any outstanding packets which have exceeded
* a predefined timeout.
*************************************************************/
-int gtp_req(struct gsn_t *gsn, int version, union gtp_packet *packet,
- int len, struct in_addr *inetaddr, void *aid) {
+int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp,
+ union gtp_packet *packet, int len,
+ struct in_addr *inetaddr, void *cbp) {
struct sockaddr_in addr;
struct qmsg_t *qmsg;
+ int fd;
+
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr = *inetaddr;
- addr.sin_port = htons(GTP0_PORT);
- packet->gtp0.h.seq = hton16(gsn->seq_next);
+ if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
+ addr.sin_port = htons(GTP0_PORT);
+ packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
+ packet->gtp0.h.seq = hton16(gsn->seq_next);
+ if (pdp)
+ packet->gtp0.h.tid = (pdp->imsi & 0x0fffffffffffffff) +
+ ((uint64_t)pdp->nsapi << 60);
+ if (pdp && ((packet->gtp0.h.type == GTP_GPDU) ||
+ (packet->gtp0.h.type == GTP_ERROR)))
+ packet->gtp0.h.flow=hton16(pdp->flru);
+ else if (pdp)
+ packet->gtp0.h.flow=hton16(pdp->flrc);
+ fd = gsn->fd0;
+ }
+ else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
+ addr.sin_port = htons(GTP1C_PORT);
+ packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT);
+ packet->gtp1l.h.seq = hton16(gsn->seq_next);
+ if (pdp && ((packet->gtp1l.h.type == GTP_GPDU) ||
+ (packet->gtp1l.h.type == GTP_ERROR)))
+ packet->gtp1l.h.tei=hton32(pdp->teid_gn);
+ else if (pdp)
+ packet->gtp1l.h.tei=hton32(pdp->teic_gn);
+ fd = gsn->fd1c;
+ } else {
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "Unknown packet flag");
+ return -1;
+ }
- if (sendto(gsn->fd, packet, len, 0,
+ if (sendto(fd, packet, len, 0,
(struct sockaddr *) &addr, sizeof(addr)) < 0) {
gsn->err_sendto++;
- gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", gsn->fd, (unsigned long) &packet, len, strerror(errno));
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", fd, (unsigned long) &packet, len, strerror(errno));
return -1;
}
@@ -425,8 +445,9 @@ int gtp_req(struct gsn_t *gsn, int version, union gtp_packet *packet,
qmsg->l = len;
qmsg->timeout = time(NULL) + 3; /* When to timeout */
qmsg->retrans = 0; /* No retransmissions so far */
- qmsg->aid = aid;
+ qmsg->cbp = cbp;
qmsg->type = ntoh8(packet->gtp0.h.type);
+ qmsg->fd = fd;
}
gsn->seq_next++; /* Count up this time */
return 0;
@@ -437,10 +458,21 @@ int gtp_req(struct gsn_t *gsn, int version, union gtp_packet *packet,
* return 0 on success, EOF if packet was not found */
int gtp_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
- union gtp_packet *packet, int len, uint8_t *type, void **aid) {
- int seq = ntoh16(packet->gtp0.h.seq);
+ union gtp_packet *packet, int len, uint8_t *type, void **cbp) {
+
+ uint16_t seq;
+
+ if ((packet->gtp0.h.flags & 0xe0) == 0x00)
+ seq = ntoh16(packet->gtp0.h.seq);
+ else if ((packet->gtp1l.h.flags & 0xe2) == 0x22)
+ seq = ntoh16(packet->gtp1l.h.seq);
+ else {
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, packet, len,
+ "Unknown GTP packet version");
+ return EOF;
+ }
- if (queue_freemsg_seq(gsn->queue_req, peer, seq, type, aid)) {
+ if (queue_freemsg_seq(gsn->queue_req, peer, seq, type, cbp)) {
gsn->err_seq++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, packet, len,
"Confirmation packet not found in queue");
@@ -462,14 +494,14 @@ int gtp_retrans(struct gsn_t *gsn) {
(qmsg->timeout <= now)) {
/*printf("Retrans timeout found: %d\n", (int) time(NULL));*/
if (qmsg->retrans > 3) { /* To many retrans */
- if (gsn->cb_conf) gsn->cb_conf(qmsg->type, EOF, NULL, qmsg->aid);
+ if (gsn->cb_conf) gsn->cb_conf(qmsg->type, EOF, NULL, qmsg->cbp);
queue_freemsg(gsn->queue_req, qmsg);
}
else {
- if (sendto(gsn->fd, &qmsg->p, qmsg->l, 0,
+ if (sendto(qmsg->fd, &qmsg->p, qmsg->l, 0,
(struct sockaddr *) &qmsg->peer, sizeof(struct sockaddr_in)) < 0) {
gsn->err_sendto++;
- gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", gsn->fd, (unsigned long) &qmsg->p, qmsg->l, strerror(errno));
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd0=%d, msg=%lx, len=%d) failed: Error = %s", gsn->fd0, (unsigned long) &qmsg->p, qmsg->l, strerror(errno));
}
queue_back(gsn->queue_req, qmsg);
qmsg->timeout = now + 3;
@@ -506,30 +538,44 @@ int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout) {
return 0;
}
-int gtp_resp(int version, struct gsn_t *gsn, union gtp_packet *packet,
- int len, struct sockaddr_in *peer) {
+int gtp_resp(int version, struct gsn_t *gsn, struct pdp_t *pdp,
+ union gtp_packet *packet, int len,
+ struct sockaddr_in *peer, int fd,
+ uint16_t seq, uint64_t tid) {
struct qmsg_t *qmsg;
- uint16_t seq;
- seq = ntoh16(packet->gtp0.h.seq);
+ if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */
+ packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE);
+ packet->gtp0.h.seq = hton16(seq);
+ packet->gtp0.h.tid = tid;
+ if (pdp && ((packet->gtp0.h.type == GTP_GPDU) ||
+ (packet->gtp0.h.type == GTP_ERROR)))
+ packet->gtp0.h.flow=hton16(pdp->flru);
+ else if (pdp)
+ packet->gtp0.h.flow=hton16(pdp->flrc);
+ }
+ else if ((packet->flags & 0xe2) == 0x22) { /* Version 1 with seq */
+ packet->gtp1l.h.length = hton16(len - GTP1_HEADER_SIZE_SHORT);
+ packet->gtp1l.h.seq = hton16(seq);
+ if (pdp && (fd == gsn->fd1u))
+ packet->gtp1l.h.tei=hton32(pdp->teid_gn);
+ else if (pdp)
+ packet->gtp1l.h.tei=hton32(pdp->teic_gn);
+ }
+ else {
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "Unknown packet flag");
+ return -1;
+ }
- /* print message */
- /*
- printf("gtp_resp: to %s:UDP%u\n",
- inet_ntoa(peer->sin_addr),
- ntohs(peer->sin_port));
- print_packet(packet, len);
- */
-
- if (fcntl(gsn->fd, F_SETFL, 0)) {
+ if (fcntl(fd, F_SETFL, 0)) {
gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()");
return -1;
}
-
- if (sendto(gsn->fd, packet, len, 0,
+
+ if (sendto(fd, packet, len, 0,
(struct sockaddr *) peer, sizeof(struct sockaddr_in)) < 0) {
gsn->err_sendto++;
- gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", gsn->fd, (unsigned long) &packet, len, strerror(errno));
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", fd, (unsigned long) &packet, len, strerror(errno));
return -1;
}
@@ -543,8 +589,9 @@ int gtp_resp(int version, struct gsn_t *gsn, union gtp_packet *packet,
qmsg->l = len;
qmsg->timeout = time(NULL) + 60; /* When to timeout */
qmsg->retrans = 0; /* No retransmissions so far */
- qmsg->aid = NULL;
+ qmsg->cbp = NULL;
qmsg->type = 0;
+ qmsg->fd = fd;
}
return 0;
}
@@ -556,27 +603,18 @@ int gtp_dublicate(struct gsn_t *gsn, int version,
if(queue_seqget(gsn->queue_resp, &qmsg, peer, seq)) {
return EOF; /* Notfound */
}
- else {
- /* print message */
-
- /*printf("gtp_dublicate: to %s:UDP%u\n",
- inet_ntoa(peer->sin_addr),
- ntohs(peer->sin_port));
- print_packet(&qmsg->p, qmsg->l);
- */
-
- if (fcntl(gsn->fd, F_SETFL, 0)) {
- gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()");
- return -1;
- }
- if (sendto(gsn->fd, &qmsg->p, qmsg->l, 0,
- (struct sockaddr *) peer, sizeof(struct sockaddr_in)) < 0) {
- gsn->err_sendto++;
- gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", gsn->fd, (unsigned long) &qmsg->p, qmsg->l, strerror(errno));
- }
- return 0;
+ if (fcntl(qmsg->fd, F_SETFL, 0)) {
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()");
+ return -1;
+ }
+
+ if (sendto(qmsg->fd, &qmsg->p, qmsg->l, 0,
+ (struct sockaddr *) peer, sizeof(struct sockaddr_in)) < 0) {
+ gsn->err_sendto++;
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", qmsg->fd, (unsigned long) &qmsg->p, qmsg->l, strerror(errno));
}
+ return 0;
}
@@ -629,7 +667,6 @@ int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
int mode)
{
struct sockaddr_in addr;
- int gtp_fd;
syslog(LOG_ERR, "GTP: gtp_newgsn() started");
@@ -649,33 +686,69 @@ int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen,
pdp_init();
/* Initialise call back functions */
- (*gsn)->cb_create_context = 0;
+ (*gsn)->cb_create_context_ind = 0;
(*gsn)->cb_delete_context = 0;
+ (*gsn)->cb_unsup_ind = 0;
(*gsn)->cb_conf = 0;
- (*gsn)->cb_gpdu = 0;
+ (*gsn)->cb_data_ind = 0;
+
+ /* Store function parameters */
+ (*gsn)->gsnc = *listen;
+ (*gsn)->gsnu = *listen;
+ (*gsn)->mode = mode;
- if ((gtp_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
+
+ /* Create GTP version 0 socket */
+ if (((*gsn)->fd0 = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
(*gsn)->err_socket++;
gtp_err(LOG_ERR, __FILE__, __LINE__, "socket(domain=%d, type=%d, protocol=%d) failed: Error = %s", AF_INET, SOCK_DGRAM, 0, strerror(errno));
return -1;
}
- (*gsn)->fd = gtp_fd;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr = *listen; /* Same IP for user traffic and signalling*/
+ addr.sin_port = htons(GTP0_PORT);
+
+ if (bind((*gsn)->fd0, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ (*gsn)->err_socket++;
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd0=%d, addr=%lx, len=%d) failed: Error = %s", (*gsn)->fd0, (unsigned long) &addr, sizeof(addr), strerror(errno));
+ return -1;
+ }
- (*gsn)->gsnc = *listen;
- (*gsn)->gsnu = *listen;
+ /* Create GTP version 1 control plane socket */
+ if (((*gsn)->fd1c = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
+ (*gsn)->err_socket++;
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "socket(domain=%d, type=%d, protocol=%d) failed: Error = %s", AF_INET, SOCK_DGRAM, 0, strerror(errno));
+ return -1;
+ }
- (*gsn)->mode = mode;
-
memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr = *listen; /* Same IP for user traffic and signalling*/
+ addr.sin_port = htons(GTP1C_PORT);
+ if (bind((*gsn)->fd1c, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ (*gsn)->err_socket++;
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd1c=%d, addr=%lx, len=%d) failed: Error = %s", (*gsn)->fd1c, (unsigned long) &addr, sizeof(addr), strerror(errno));
+ return -1;
+ }
+
+ /* Create GTP version 1 user plane socket */
+ if (((*gsn)->fd1u = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
+ (*gsn)->err_socket++;
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "socket(domain=%d, type=%d, protocol=%d) failed: Error = %s", AF_INET, SOCK_DGRAM, 0, strerror(errno));
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
- /* addr.sin_addr = *inetaddr; */
addr.sin_addr = *listen; /* Same IP for user traffic and signalling*/
- addr.sin_port = htons(GTP0_PORT);
+ addr.sin_port = htons(GTP1U_PORT);
- if (bind(gtp_fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ if (bind((*gsn)->fd1u, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
(*gsn)->err_socket++;
- gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd=%d, addr=%lx, len=%d) failed: Error = %s", gtp_fd, (unsigned long) &addr, sizeof(addr), strerror(errno));
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "bind(fd1c=%d, addr=%lx, len=%d) failed: Error = %s", (*gsn)->fd1c, (unsigned long) &addr, sizeof(addr), strerror(errno));
return -1;
}
@@ -687,6 +760,10 @@ int gtp_free(struct gsn_t *gsn) {
/* Clean up retransmit queues */
queue_free(gsn->queue_req);
queue_free(gsn->queue_resp);
+
+ close(gsn->fd0);
+ close(gsn->fd1c);
+ close(gsn->fd1u);
free(gsn);
return 0;
@@ -722,67 +799,56 @@ int gtp_free(struct gsn_t *gsn) {
*************************************************************/
/* Send off an echo request */
-int gtp_echo_req(struct gsn_t *gsn, struct in_addr *inetaddr)
+int gtp_echo_req(struct gsn_t *gsn, int version, void *cbp,
+ struct in_addr *inetaddr)
{
union gtp_packet packet;
-
- get_default_gtp(0, &packet);
- packet.gtp0.h.type = hton8(GTP_ECHO_REQ);
- packet.gtp0.h.length = hton16(0);
-
- return gtp_req(gsn, 0, &packet, GTP0_HEADER_SIZE, inetaddr, NULL);
+ int length = get_default_gtp(version, GTP_ECHO_REQ, &packet);
+ return gtp_req(gsn, version, NULL, &packet, length, inetaddr, cbp);
}
-/* Send of an echo reply */
-int gtp_echo_resp(struct gsn_t *gsn, struct sockaddr_in *peer,
+/* Send off an echo reply */
+int gtp_echo_resp(struct gsn_t *gsn, int version,
+ struct sockaddr_in *peer, int fd,
void *pack, unsigned len)
{
union gtp_packet packet;
- int length = 0;
-
- get_default_gtp(0, &packet);
-
- gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_RECOVERY,
- gsn->restart_counter);
-
- packet.gtp0.h.type = hton8(GTP_ECHO_RSP);
- packet.gtp0.h.length = hton16(length);
- packet.gtp0.h.seq = ((union gtp_packet*)pack)->gtp0.h.seq;
-
- return gtp_resp(0, gsn, &packet, GTP0_HEADER_SIZE+length, peer);
+ int length = get_default_gtp(version, GTP_ECHO_RSP, &packet);
+ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY, gsn->restart_counter);
+ return gtp_resp(version, gsn, NULL, &packet, length, peer, fd,
+ get_seq(pack), get_tid(pack));
}
/* Handle a received echo request */
-int gtp_echo_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
- void *pack, unsigned len) {
+int gtp_echo_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
+ int fd, void *pack, unsigned len) {
- uint16_t seq = ntoh16(((union gtp_packet*)pack)->gtp0.h.seq);
+ /* Check if it was a dublicate request */
+ if(!gtp_dublicate(gsn, 0, peer, get_seq(pack))) return 0;
- if(!gtp_dublicate(gsn, 0, peer, seq)) {
- return 0; /* We allready send of response once */
- }
-
-
- /* Now send off a reply to the peer */
- return gtp_echo_resp(gsn, peer, pack, len);
+ /* Send off reply to request */
+ return gtp_echo_resp(gsn, version, peer, fd, pack, len);
}
/* Handle a received echo reply */
-int gtp_echo_conf(struct gsn_t *gsn, struct sockaddr_in *peer,
+int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
void *pack, unsigned len) {
union gtpie_member *ie[GTPIE_SIZE];
unsigned char recovery;
- void *aid = NULL;
+ void *cbp = NULL;
uint8_t type = 0;
+ int hlen = get_hlen(pack);
/* Remove packet from queue */
- if (gtp_conf(gsn, 0, peer, pack, len, &type, &aid)) return EOF;
+ if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp)) return EOF;
- if (gtpie_decaps(ie, pack+sizeof(struct gtp0_header), len-sizeof(struct gtp0_header))) {
+ /* Extract information elements into a pointer array */
+ if (gtpie_decaps(ie, version, pack+hlen, len-hlen)) {
gsn->invalid++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Invalid message format");
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, NULL, cbp);
return EOF;
}
@@ -790,10 +856,13 @@ int gtp_echo_conf(struct gsn_t *gsn, struct sockaddr_in *peer,
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing mandatory field");
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, NULL, cbp);
return EOF;
}
- if (gsn->cb_conf) gsn->cb_conf(type, 0, NULL, aid); /* TODO: Should return recovery in callback */
+ /* Echo reply packages does not have a cause information element */
+ /* Instead we return the recovery number in the callback function */
+ if (gsn->cb_conf) gsn->cb_conf(type, recovery, NULL, cbp);
return 0;
}
@@ -809,34 +878,22 @@ int gtp_echo_conf(struct gsn_t *gsn, struct sockaddr_in *peer,
* only listen to the GTP0 port, and therefore will never receive
* anything else than GTP0 */
-int gtp_unsup_resp(struct gsn_t *gsn, struct sockaddr_in *peer,
- void *pack, unsigned len)
+int gtp_unsup_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer,
+ int fd, void *pack, unsigned len)
{
union gtp_packet packet;
- int length = 0;
- get_default_gtp(0, &packet);
- packet.gtp0.h.type = hton8(GTP_NOT_SUPPORTED);
- packet.gtp0.h.length = hton16(0);
-
- return gtp_resp(0, gsn, &packet, GTP0_HEADER_SIZE+length, peer);
+ /* GTP 1 is the highest supported protocol */
+ int hlen = get_default_gtp(1, GTP_NOT_SUPPORTED, &packet);
+ return gtp_resp(version, gsn, NULL, &packet, hlen, peer, fd, 0, 0);
}
/* Handle a Version Not Supported message */
-int gtp_unsup_conf(struct gsn_t *gsn, struct sockaddr_in *peer, void *pack, unsigned len) {
+int gtp_unsup_ind(struct gsn_t *gsn, struct sockaddr_in *peer,
+ void *pack, unsigned len) {
- /* TODO: Need to check the validity of header and information elements */
- /* TODO: Implement callback to application */
- /* As long as we only support GTP0 we should never receive this message */
- /* Should be implemented as part of GTP1 support */
+ if (gsn->cb_unsup_ind) gsn->cb_unsup_ind(peer);
- /* print received message */
- /*
- printf("gtp_unsup_ind: from %s:UDP%u\n",
- inet_ntoa(peer->sin_addr),
- ntohs(peer->sin_port));
- print_packet(pack, len);
- */
return 0;
}
@@ -852,284 +909,404 @@ int gtp_unsup_conf(struct gsn_t *gsn, struct sockaddr_in *peer, void *pack, unsi
* information.
*************************************************************/
-/* Send Create PDP Context Request */
-extern int gtp_create_pdp_req(struct gsn_t *gsn, int version, void *aid,
- struct in_addr* inetaddr, struct pdp_t *pdp) {
+/* API: Send Create PDP Context Request */
+extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp,
+ void *cbp, struct in_addr* inetaddr) {
union gtp_packet packet;
- int length = 0;
+ int length = get_default_gtp(pdp->version, GTP_CREATE_PDP_REQ, &packet);
- get_default_gtp(0, &packet);
-
- if (0==0) { /* Always GTP0 */
-
- gtpie_tv0(packet.gtp0.p, &length, GTP_MAX, GTPIE_QOS_PROFILE0,
+ if (pdp->version == 0)
+ gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE0,
sizeof(pdp->qos_req0), pdp->qos_req0);
- gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_RECOVERY,
- gsn->restart_counter);
- gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_SELECTION_MODE,
- pdp->selmode);
- gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_DI,
- pdp->fllu);
- gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_C,
- pdp->fllc);
- gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_EUA,
- pdp->eua.l, pdp->eua.v);
- gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_APN,
- pdp->apn_use.l, pdp->apn_use.v);
- if (pdp->pco_req.l) { /* Optional PCO */
- gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_PCO,
- pdp->pco_req.l, pdp->pco_req.v);
- }
+ if (pdp->version == 1)
+ gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_IMSI,
+ sizeof(pdp->imsi), (uint8_t*) &pdp->imsi);
- gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR,
- pdp->gsnlc.l, pdp->gsnlc.v);
- gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR,
- pdp->gsnlu.l, pdp->gsnlu.v);
- gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_MSISDN,
- pdp->msisdn.l, pdp->msisdn.v);
-
+ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY,
+ gsn->restart_counter);
+ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_SELECTION_MODE,
+ pdp->selmode);
+ if (pdp->version == 0) {
+ gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_DI,
+ pdp->fllu);
+ gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_C,
+ pdp->fllc);
+ }
- } else { /* GTP1 */
- gtpie_tv0(packet.gtp1s.p, &length, GTP_MAX, GTPIE_IMSI,
- sizeof(pdp->imsi), (uint8_t*) &pdp->imsi);
- gtpie_tv1(packet.gtp1s.p, &length, GTP_MAX, GTPIE_RECOVERY,
- gsn->restart_counter);
- gtpie_tv1(packet.gtp1s.p, &length, GTP_MAX, GTPIE_SELECTION_MODE,
- pdp->selmode);
- gtpie_tv4(packet.gtp1s.p, &length, GTP_MAX, GTPIE_TEI_DI,
+ if (pdp->version == 1) {
+ gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_DI,
pdp->teid_own);
- gtpie_tv4(packet.gtp1s.p, &length, GTP_MAX, GTPIE_TEI_C,
+ gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_C,
pdp->teic_own);
- gtpie_tv1(packet.gtp1s.p, &length, GTP_MAX, GTPIE_NSAPI,
- pdp->nsapi);
- /*gtpie_tv1(packet.gtp1s.p, &length, GTP_MAX, GTPIE_NSAPI,
- pdp->nsapil); For use by several QoS profiles for the same address */
- gtpie_tv2(packet.gtp1s.p, &length, GTP_MAX, GTPIE_CHARGING_C,
+ }
+
+ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI,
+ pdp->nsapi);
+
+ /*gtpie_tv1(packet.gtp1l.p, &length, GTP_MAX, GTPIE_NSAPI,
+ pdp->nsapil); For use by several QoS profiles for the same address */
+
+ if (pdp->version == 1) {
+ gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_CHARGING_C,
pdp->cch_pdp);
- gtpie_tv2(packet.gtp1s.p, &length, GTP_MAX, GTPIE_TRACE_REF,
- pdp->traceref);
- gtpie_tv2(packet.gtp1s.p, &length, GTP_MAX, GTPIE_TRACE_TYPE,
- pdp->tracetype);
- gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_EUA,
- pdp->eua.l, pdp->eua.v);
- gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_APN,
- pdp->apn_use.l, pdp->apn_use.v);
- gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_PCO,
+ }
+
+ /* TODO
+ gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_REF,
+ pdp->traceref);
+ gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_TYPE,
+ pdp->tracetype); */
+
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
+ pdp->eua.l, pdp->eua.v);
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_APN,
+ pdp->apn_use.l, pdp->apn_use.v);
+
+ if (pdp->pco_req.l)
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_PCO,
pdp->pco_req.l, pdp->pco_req.v);
- gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_GSN_ADDR,
- pdp->gsnlc.l, pdp->gsnlc.v);
- gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_GSN_ADDR,
- pdp->gsnlu.l, pdp->gsnlu.v);
- gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_MSISDN,
- pdp->msisdn.l, pdp->msisdn.v);
- gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_QOS_PROFILE,
+
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR,
+ pdp->gsnlc.l, pdp->gsnlc.v);
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR,
+ pdp->gsnlu.l, pdp->gsnlu.v);
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_MSISDN,
+ pdp->msisdn.l, pdp->msisdn.v);
+
+ if (pdp->version == 1)
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE,
pdp->qos_req.l, pdp->qos_req.v);
- gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_TFT,
+
+
+ if ((pdp->version == 1) && pdp->tft.l)
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_TFT,
pdp->tft.l, pdp->tft.v);
- gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_TRIGGER_ID,
+
+ if ((pdp->version == 1) && pdp->triggerid.l)
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_TRIGGER_ID,
pdp->triggerid.l, pdp->triggerid.v);
- gtpie_tlv(packet.gtp1s.p, &length, GTP_MAX, GTPIE_OMC_ID,
+
+ if ((pdp->version == 1) && pdp->omcid.l)
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_OMC_ID,
pdp->omcid.l, pdp->omcid.v);
- }
- packet.gtp0.h.type = hton8(GTP_CREATE_PDP_REQ);
- packet.gtp0.h.length = hton16(length);
- packet.gtp0.h.flow = 0;
- packet.gtp0.h.tid = pdp->tid;
+
+ gtp_req(gsn, pdp->version, pdp, &packet, length, inetaddr, cbp);
+
+ return 0;
+}
+
- gtp_req(gsn, 0, &packet, GTP0_HEADER_SIZE+length, inetaddr, aid);
+/* API: Application response to context indication */
+int gtp_create_context_resp(struct gsn_t *gsn, struct pdp_t *pdp, int cause) {
+ /* Now send off a reply to the peer */
+ gtp_create_pdp_resp(gsn, pdp->version, pdp, cause);
+
+ if (cause != GTPCAUSE_ACC_REQ) {
+ pdp_freepdp(pdp);
+ }
+
return 0;
}
-/* Send Create PDP Context Response */
-int gtp_create_pdp_resp(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer,
- void *pack, unsigned len,
- struct pdp_t *pdp, uint8_t cause)
+/* API: Register create context indication callback */
+int gtp_set_cb_create_context_ind(struct gsn_t *gsn,
+ int (*cb_create_context_ind) (struct pdp_t* pdp))
{
- union gtp_packet packet;
- int length = 0;
+ gsn->cb_create_context_ind = cb_create_context_ind;
+ return 0;
+}
- get_default_gtp(0, &packet);
- gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_CAUSE, cause);
+/* Send Create PDP Context Response */
+int gtp_create_pdp_resp(struct gsn_t *gsn, int version, struct pdp_t *pdp,
+ uint8_t cause) {
+ union gtp_packet packet;
+ int length = get_default_gtp(version, GTP_CREATE_PDP_RSP, &packet);
+
+ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_CAUSE, cause);
if (cause == GTPCAUSE_ACC_REQ) {
- gtpie_tv0(packet.gtp0.p, &length, GTP_MAX, GTPIE_QOS_PROFILE0,
- sizeof(pdp->qos_neg0), pdp->qos_neg0);
- gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_REORDER,
+
+ if (version == 0)
+ gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE0,
+ sizeof(pdp->qos_neg0), pdp->qos_neg0);
+
+ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_REORDER,
pdp->reorder);
- gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_RECOVERY,
+ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY,
gsn->restart_counter);
- gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_DI,
- pdp->fllu);
- gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_C,
- pdp->fllc);
- gtpie_tv4(packet.gtp0.p, &length, GTP_MAX, GTPIE_CHARGING_ID,
+
+ if (version == 0) {
+ gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_DI,
+ pdp->fllu);
+ gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_C,
+ pdp->fllc);
+ }
+
+ if (version == 1) {
+ gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_DI,
+ pdp->teid_own);
+ gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_C,
+ pdp->teic_own);
+ }
+
+ gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_CHARGING_ID,
0x12345678);
- gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_EUA,
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
pdp->eua.l, pdp->eua.v);
if (pdp->pco_neg.l) { /* Optional PCO */
- gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_PCO,
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_PCO,
pdp->pco_neg.l, pdp->pco_neg.v);
}
- gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR,
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR,
pdp->gsnlc.l, pdp->gsnlc.v);
- gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR,
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR,
pdp->gsnlu.l, pdp->gsnlu.v);
- }
- packet.gtp0.h.type = hton8(GTP_CREATE_PDP_RSP);
- packet.gtp0.h.length = hton16(length);
- packet.gtp0.h.flow = hton16(pdp->flrc);
- packet.gtp0.h.seq = ((union gtp_packet*)pack)->gtp0.h.seq;
- packet.gtp0.h.tid = ((union gtp_packet*)pack)->gtp0.h.tid;
+ if (version == 1)
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE,
+ pdp->qos_neg.l, pdp->qos_neg.v);
+
+ /* TODO: Charging gateway address */
+ }
- return gtp_resp(0, gsn, &packet, GTP0_HEADER_SIZE+length, peer);
+ return gtp_resp(version, gsn, pdp, &packet, length, &pdp->sa_peer,
+ pdp->fd, pdp->seq, pdp->tid);
}
/* Handle Create PDP Context Request */
int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, void *pack, unsigned len) {
+ struct sockaddr_in *peer, int fd,
+ void *pack, unsigned len) {
struct pdp_t *pdp, *pdp_old;
struct pdp_t pdp_buf;
union gtpie_member* ie[GTPIE_SIZE];
uint8_t recovery;
- uint64_t imsi;
- uint8_t nsapi;
- int auth = 0; /* Allow access if no callback is defined */
- uint16_t seq = ntoh16(((union gtp_packet*)pack)->gtp0.h.seq);
+ uint16_t seq = get_seq(pack);
+ int hlen = get_hlen(pack);
- if(!gtp_dublicate(gsn, 0, peer, seq)) {
- return 0; /* We allready send of response once */
+ if(!gtp_dublicate(gsn, 0, peer, seq)) return 0;
+
+ pdp = &pdp_buf;
+ memset(pdp, 0, sizeof(struct pdp_t));
+
+ if (version == 0) {
+ pdp->imsi = ((union gtp_packet*)pack)->gtp0.h.tid & 0x0fffffffffffffff;
+ pdp->nsapi = (((union gtp_packet*)pack)->gtp0.h.tid & 0xf000000000000000) >> 60;
}
+ pdp->seq = seq;
+ pdp->sa_peer = *peer;
+ pdp->fd = fd;
+ pdp->version = version;
+
/* Decode information elements */
- if (gtpie_decaps(ie, pack+GTP0_HEADER_SIZE, len-GTP0_HEADER_SIZE)) {
+ if (gtpie_decaps(ie, version, pack+hlen, len-hlen)) {
gsn->invalid++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Invalid message format");
if (0 == version)
return EOF;
else
- return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp,
- GTPCAUSE_INVALID_MESSAGE);
+ return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_INVALID_MESSAGE);
}
- pdp = &pdp_buf;
- memset(pdp, 0, sizeof(struct pdp_t));
-
- /* Extract IMSI and NSAPI from header */
- imsi = ((union gtp_packet*)pack)->gtp0.h.tid & 0x0fffffffffffffff;
- nsapi = (((union gtp_packet*)pack)->gtp0.h.tid & 0xf000000000000000) >> 60;
+ if (version == 0) {
+ if (gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0,
+ pdp->qos_req0, sizeof(pdp->qos_req0))) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_MAN_IE_MISSING);
+ }
+ }
- /* pdp_newpdp(&pdp, imsi, nsapi); TODO: Need to remove again */
- if (gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0,
- pdp->qos_req0, sizeof(pdp->qos_req0))) {
- gsn->missing++;
- gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
- "Missing mandatory information field");
- return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp,
- GTPCAUSE_MAN_IE_MISSING);
+ if (version == 1) {
+ /* IMSI (conditional) */
+ if (gtpie_gettv0(ie, GTPIE_IMSI, 0, &pdp->imsi, sizeof(pdp->imsi))) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ return gtp_create_pdp_resp(gsn, version, pdp,
+ GTPCAUSE_MAN_IE_MISSING);
+ }
}
- /* Extract recovery (optional) */
+ /* Recovery (optional) */
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
/* TODO: Handle received recovery IE */
}
+ /* Selection mode (conditional) */
if (gtpie_gettv0(ie, GTPIE_SELECTION_MODE, 0,
&pdp->selmode, sizeof(pdp->selmode))) {
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing mandatory information field");
- return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp,
+ return gtp_create_pdp_resp(gsn, version, pdp,
GTPCAUSE_MAN_IE_MISSING);
}
- if (gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp->flru)) {
- gsn->missing++;
- gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
- "Missing mandatory information field");
- return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp,
- GTPCAUSE_MAN_IE_MISSING);
+ if (version == 0) {
+ if (gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp->flru)) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ return gtp_create_pdp_resp(gsn, version, pdp,
+ GTPCAUSE_MAN_IE_MISSING);
+ }
+
+ if (gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp->flrc)) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ return gtp_create_pdp_resp(gsn, version, pdp,
+ GTPCAUSE_MAN_IE_MISSING);
+ }
}
- if (gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp->flrc)) {
- gsn->missing++;
- gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
- "Missing mandatory information field");
- return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp,
- GTPCAUSE_MAN_IE_MISSING);
+
+ if (version == 1) {
+ /* TEID (mandatory) */
+ if (gtpie_gettv4(ie, GTPIE_TEI_DI, 0, &pdp->teid_gn)) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ return gtp_create_pdp_resp(gsn, version, pdp,
+ GTPCAUSE_MAN_IE_MISSING);
+ }
+
+ /* TEIC (conditional) */
+ if (gtpie_gettv4(ie, GTPIE_TEI_C, 0, &pdp->teic_gn)) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ return gtp_create_pdp_resp(gsn, version, pdp,
+ GTPCAUSE_MAN_IE_MISSING);
+ }
+
+ /* NSAPI (mandatory) */
+ if (gtpie_gettv1(ie, GTPIE_NSAPI, 0, &pdp->nsapi)) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ return gtp_create_pdp_resp(gsn, version, pdp,
+ GTPCAUSE_MAN_IE_MISSING);
+ }
+
+ /* Linked NSAPI (conditional) */
+ if (gtpie_gettv1(ie, GTPIE_NSAPI, 1, &pdp->linked_nsapi)) {
+ /* TODO: Handle linked NSAPI */
+ /* Currently the Secondary PDP Context Activation Procedure is not */
+ /* supported */
+ } else {
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Found Linked NSAPI");
+ return gtp_create_pdp_resp(gsn, version, pdp,
+ GTPCAUSE_NOT_SUPPORTED);
+ }
}
+ /* Charging Characteriatics (optional) */
+ /* Trace reference (optional) */
+ /* Trace type (optional) */
+ /* Charging Characteriatics (optional) */
+
+ /* End User Address (conditional) */
if (gtpie_gettlv(ie, GTPIE_EUA, 0, &pdp->eua.l,
&pdp->eua.v, sizeof(pdp->eua.v))) {
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing mandatory information field");
- return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp,
+ return gtp_create_pdp_resp(gsn, version, pdp,
GTPCAUSE_MAN_IE_MISSING);
}
+ /* APN */
if (gtpie_gettlv(ie, GTPIE_APN, 0, &pdp->apn_req.l,
&pdp->apn_req.v, sizeof(pdp->apn_req.v))) {
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing mandatory information field");
- return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp,
+ return gtp_create_pdp_resp(gsn, version, pdp,
GTPCAUSE_MAN_IE_MISSING);
}
/* Extract protocol configuration options (optional) */
if (!gtpie_gettlv(ie, GTPIE_PCO, 0, &pdp->pco_req.l,
&pdp->pco_req.v, sizeof(pdp->pco_req.v))) {
- /* TODO: Handle PCO IE */
}
+ /* SGSN address for signalling (mandatory) */
if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 0, &pdp->gsnrc.l,
&pdp->gsnrc.v, sizeof(pdp->gsnrc.v))) {
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing mandatory information field");
- return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp,
+ return gtp_create_pdp_resp(gsn, version, pdp,
GTPCAUSE_MAN_IE_MISSING);
}
+ /* SGSN address for user traffic (mandatory) */
if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 1, &pdp->gsnru.l,
&pdp->gsnru.v, sizeof(pdp->gsnru.v))) {
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing mandatory information field");
- return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp,
+ return gtp_create_pdp_resp(gsn, version, pdp,
GTPCAUSE_MAN_IE_MISSING);
}
+ /* MSISDN (conditional) */
if (gtpie_gettlv(ie, GTPIE_MSISDN, 0, &pdp->msisdn.l,
&pdp->msisdn.v, sizeof(pdp->msisdn.v))) {
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing mandatory information field");
- return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp,
+ return gtp_create_pdp_resp(gsn, version, pdp,
GTPCAUSE_MAN_IE_MISSING);
}
+ if (version == 1) {
+ /* QoS (mandatory) */
+ if (gtpie_gettlv(ie, GTPIE_QOS_PROFILE, 0, &pdp->qos_req.l,
+ &pdp->qos_req.v, sizeof(pdp->qos_req.v))) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ return gtp_create_pdp_resp(gsn, version, pdp,
+ GTPCAUSE_MAN_IE_MISSING);
+ }
+
+ /* TFT (conditional) */
+ if (gtpie_gettlv(ie, GTPIE_TFT, 0, &pdp->tft.l,
+ &pdp->tft.v, sizeof(pdp->tft.v))) {
+ }
+
+ /* Trigger ID */
+ /* OMC identity */
+ }
+
+ /* Initialize our own IP addresses */
in_addr2gsna(&pdp->gsnlc, &gsn->gsnc);
in_addr2gsna(&pdp->gsnlu, &gsn->gsnu);
if (GTP_DEBUG) printf("gtp_create_pdp_ind: Before pdp_tidget\n");
- if (!pdp_tidget(&pdp_old, ((union gtp_packet*)pack)->gtp0.h.tid)) {
+ if (!pdp_getimsi(&pdp_old, pdp->imsi, pdp->nsapi)) {
/* Found old pdp with same tid. Now the voodoo begins! */
+ /* 09.60 / 29.060 allows create on existing context to "steal" */
+ /* the context which was allready established */
/* We check that the APN, selection mode and MSISDN is the same */
if (GTP_DEBUG) printf("gtp_create_pdp_ind: Old context found\n");
- if ( (pdp->apn_req.l == pdp_old->apn_req.l)
+ if ((pdp->apn_req.l == pdp_old->apn_req.l)
&& (!memcmp(pdp->apn_req.v, pdp_old->apn_req.v, pdp->apn_req.l))
&& (pdp->selmode == pdp_old->selmode)
&& (pdp->msisdn.l == pdp_old->msisdn.l)
@@ -1147,18 +1324,20 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
pdp_old->flru = pdp->flru;
pdp_old->flrc = pdp->flrc;
+ /* Copy remote tei */
+ pdp_old->teid_gn = pdp->teid_gn;
+ pdp_old->teic_gn = pdp->teic_gn;
+
/* Copy peer GSN address */
pdp_old->gsnrc.l = pdp->gsnrc.l;
memcpy(&pdp_old->gsnrc.v, &pdp->gsnrc.v, pdp->gsnrc.l);
pdp_old->gsnru.l = pdp->gsnru.l;
memcpy(&pdp_old->gsnru.v, &pdp->gsnru.v, pdp->gsnru.l);
- /* pdp_freepdp(pdp); not nessasary anymore since never allocated */
pdp = pdp_old;
/* Confirm to peer that things were "successful" */
- return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp,
- GTPCAUSE_ACC_REQ);
+ return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_ACC_REQ);
}
else { /* This is not the same PDP context. Delete the old one. */
@@ -1166,59 +1345,53 @@ int gtp_create_pdp_ind(struct gsn_t *gsn, int version,
if (gsn->cb_delete_context) gsn->cb_delete_context(pdp_old);
pdp_freepdp(pdp_old);
-
+
if (GTP_DEBUG) printf("gtp_create_pdp_ind: Deleted...\n");
}
}
- pdp_newpdp(&pdp, imsi, nsapi, pdp);
+ pdp_newpdp(&pdp, pdp->imsi, pdp->nsapi, pdp);
/* Callback function to validata login */
- if (gsn->cb_create_context !=0)
- auth = gsn->cb_create_context(pdp);
-
- /* Now send off a reply to the peer */
- if (!auth) {
- return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp,
- GTPCAUSE_ACC_REQ);
- }
+ if (gsn->cb_create_context_ind !=0)
+ return gsn->cb_create_context_ind(pdp);
else {
- gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp,
- GTPCAUSE_USER_AUTH_FAIL);
- pdp_freepdp(pdp);
- return 0;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "No create_context_ind callback defined");
+ return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_NOT_SUPPORTED);
}
}
/* Handle Create PDP Context Response */
int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer,
- void *pack, unsigned len) {
+ struct sockaddr_in *peer,
+ void *pack, unsigned len) {
struct pdp_t *pdp;
union gtpie_member *ie[GTPIE_SIZE];
uint8_t cause, recovery;
- void *aid = NULL;
+ void *cbp = NULL;
uint8_t type = 0;
+ int hlen = get_hlen(pack);
/* Remove packet from queue */
- if (gtp_conf(gsn, 0, peer, pack, len, &type, &aid)) return EOF;
+ if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp)) return EOF;
/* Find the context in question */
- if (pdp_getgtp0(&pdp, ntoh16(((union gtp_packet*)pack)->gtp0.h.flow))) {
+ if (pdp_getgtp1(&pdp, get_tei(pack))) {
gsn->err_unknownpdp++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Unknown PDP context");
- if (gsn->cb_conf) gsn->cb_conf(type, EOF, NULL, aid);
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, NULL, cbp);
return EOF;
}
/* Decode information elements */
- if (gtpie_decaps(ie, pack+GTP0_HEADER_SIZE, len-GTP0_HEADER_SIZE)) {
+ if (gtpie_decaps(ie, version, pack+hlen, len-hlen)) {
gsn->invalid++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Invalid message format");
- if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
return EOF;
}
@@ -1227,7 +1400,7 @@ int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing mandatory information field");
- if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
return EOF;
}
@@ -1239,53 +1412,71 @@ int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
/* Extract protocol configuration options (optional) */
if (!gtpie_gettlv(ie, GTPIE_PCO, 0, &pdp->pco_req.l,
&pdp->pco_req.v, sizeof(pdp->pco_req.v))) {
- /* TODO: Handle PCO IE */
}
/* Check all conditional information elements */
if (GTPCAUSE_ACC_REQ == cause) {
- if (gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0, /* TODO: HACK only gtp0 */
- &pdp->qos_neg0, sizeof(pdp->qos_neg0))) {
- gsn->missing++;
- gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
- "Missing conditional information field");
- if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
- return EOF;
+ if (version == 0) {
+ if (gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0,
+ &pdp->qos_neg0, sizeof(pdp->qos_neg0))) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing conditional information field");
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
+ return EOF;
+ }
}
- /* pdp->qos_neg.l = 3; * TODO: HACK only gtp0 */
if (gtpie_gettv1(ie, GTPIE_REORDER, 0, &pdp->reorder)) {
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing conditional information field");
- if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
return EOF;
}
- if (gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp->flru)) {
- gsn->missing++;
- gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
- "Missing conditional information field");
- if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
- return EOF;
+ if (version == 0) {
+ if (gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp->flru)) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing conditional information field");
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
+ return EOF;
+ }
+
+ if (gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp->flrc)) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing conditional information field");
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
+ return EOF;
+ }
}
+
+ if (version == 1) {
+ if (gtpie_gettv4(ie, GTPIE_TEI_DI, 0, &pdp->teid_gn)) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing conditional information field");
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
+ return EOF;
+ }
- if (gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp->flrc)) {
- gsn->missing++;
- gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
- "Missing conditional information field");
- if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
- return EOF;
+ if (gtpie_gettv4(ie, GTPIE_TEI_C, 0, &pdp->teic_gn)) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing conditional information field");
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
+ return EOF;
+ }
}
if (gtpie_gettv4(ie, GTPIE_CHARGING_ID, 0, &pdp->cid)) {
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing conditional information field");
- if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
- return gtp_create_pdp_resp(gsn, version, peer, pack, len, pdp,
- GTPCAUSE_MAN_IE_MISSING);
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
}
if (gtpie_gettlv(ie, GTPIE_EUA, 0, &pdp->eua.l,
@@ -1293,7 +1484,7 @@ int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing conditional information field");
- if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
return EOF;
}
@@ -1302,7 +1493,7 @@ int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing conditional information field");
- if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
return EOF;
}
@@ -1311,178 +1502,375 @@ int gtp_create_pdp_conf(struct gsn_t *gsn, int version,
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing conditional information field");
- if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
return EOF;
}
- }
- if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, aid);
+ if (version == 1) {
+ if (gtpie_gettlv(ie, GTPIE_QOS_PROFILE, 0, &pdp->qos_neg.l,
+ &pdp->qos_neg.v, sizeof(pdp->qos_neg.v))) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing conditional information field");
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
+ return EOF;
+ }
+ }
+
+ }
+
+ if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, cbp);
return 0;
}
-/* Send Update PDP Context Request */
-extern int gtp_update_pdp_req(struct gsn_t *gsn, int version, void *aid,
- struct in_addr* inetaddr, struct pdp_t *pdp) {
- union gtp_packet packet;
- int length = 0;
- get_default_gtp(0, &packet);
+/* API: Send Update PDP Context Request */
+int gtp_update_context(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp,
+ struct in_addr* inetaddr) {
+ union gtp_packet packet;
+ int length = get_default_gtp(pdp->version, GTP_UPDATE_PDP_REQ, &packet);
- gtpie_tv0(packet.gtp0.p, &length, GTP_MAX, GTPIE_QOS_PROFILE0,
- sizeof(pdp->qos_req0), pdp->qos_req0);
- gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_RECOVERY,
+ if (pdp->version == 0)
+ gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE0,
+ sizeof(pdp->qos_req0), pdp->qos_req0);
+
+ if (pdp->version == 1)
+ gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_IMSI,
+ sizeof(pdp->imsi), (uint8_t*) &pdp->imsi);
+
+ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY,
gsn->restart_counter);
- gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_DI,
- pdp->fllu);
- gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_C,
- pdp->fllc);
- gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR,
+
+ if (pdp->version == 0) {
+ gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_DI,
+ pdp->fllu);
+ gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_C,
+ pdp->fllc);
+ }
+
+ if (pdp->version == 1) {
+ gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_DI,
+ pdp->teid_own);
+ gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_C,
+ pdp->teic_own);
+ }
+
+ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI,
+ pdp->nsapi);
+
+ /* TODO
+ gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_REF,
+ pdp->traceref);
+ gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_TYPE,
+ pdp->tracetype); */
+
+ /* TODO if ggsn update message
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
+ pdp->eua.l, pdp->eua.v);
+ */
+
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR,
pdp->gsnlc.l, pdp->gsnlc.v);
- gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR,
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR,
pdp->gsnlu.l, pdp->gsnlu.v);
+
+ if (pdp->version == 1)
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE,
+ pdp->qos_req.l, pdp->qos_req.v);
+
+
+ if ((pdp->version == 1) && pdp->tft.l)
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_TFT,
+ pdp->tft.l, pdp->tft.v);
- packet.gtp0.h.type = hton8(GTP_UPDATE_PDP_REQ);
- packet.gtp0.h.length = hton16(length);
- packet.gtp0.h.flow = 0;
- packet.gtp0.h.tid = (pdp->imsi & 0x0fffffffffffffff) + ((uint64_t)pdp->nsapi << 60);
+ if ((pdp->version == 1) && pdp->triggerid.l)
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_TRIGGER_ID,
+ pdp->triggerid.l, pdp->triggerid.v);
+
+ if ((pdp->version == 1) && pdp->omcid.l)
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_OMC_ID,
+ pdp->omcid.l, pdp->omcid.v);
+
+ gtp_req(gsn, pdp->version, NULL, &packet, length, inetaddr, cbp);
- return gtp_req(gsn, 0, &packet, GTP0_HEADER_SIZE+length, inetaddr, aid);
+ return 0;
}
+
/* Send Update PDP Context Response */
-int gtp_update_pdp_resp(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer,
- void *pack, unsigned len,
- struct pdp_t *pdp, uint8_t cause)
-{
+int gtp_update_pdp_resp(struct gsn_t *gsn, int version,
+ struct sockaddr_in *peer, int fd,
+ void *pack, unsigned len,
+ struct pdp_t *pdp, uint8_t cause) {
+
union gtp_packet packet;
- int length = 0;
+ int length = get_default_gtp(version, GTP_CREATE_PDP_RSP, &packet);
+
+ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_CAUSE, cause);
+
+ if (cause == GTPCAUSE_ACC_REQ) {
+
+ if (version == 0)
+ gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE0,
+ sizeof(pdp->qos_neg0), pdp->qos_neg0);
- get_default_gtp(0, &packet);
+ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY,
+ gsn->restart_counter);
- gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_CAUSE, cause);
+ if (version == 0) {
+ gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_DI,
+ pdp->fllu);
+ gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_FL_C,
+ pdp->fllc);
+ }
- if (cause == GTPCAUSE_ACC_REQ) {
- gtpie_tv0(packet.gtp0.p, &length, GTP_MAX, GTPIE_QOS_PROFILE0,
- sizeof(pdp->qos_sub0), pdp->qos_sub0);
- gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_RECOVERY,
- gsn->restart_counter);
- gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_DI,
- pdp->fllu);
- gtpie_tv2(packet.gtp0.p, &length, GTP_MAX, GTPIE_FL_C,
- pdp->fllc);
- gtpie_tv4(packet.gtp0.p, &length, GTP_MAX, GTPIE_CHARGING_ID,
- 0x12345678);
- gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR,
+ if (version == 1) {
+ gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_DI,
+ pdp->teid_own);
+ gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_C,
+ pdp->teic_own);
+ }
+
+ gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_CHARGING_ID,
+ 0x12345678); /* TODO */
+
+ /* If ggsn
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA,
+ pdp->eua.l, pdp->eua.v); */
+
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR,
pdp->gsnlc.l, pdp->gsnlc.v);
- gtpie_tlv(packet.gtp0.p, &length, GTP_MAX, GTPIE_GSN_ADDR,
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR,
pdp->gsnlu.l, pdp->gsnlu.v);
- }
- packet.gtp0.h.type = hton8(GTP_UPDATE_PDP_RSP);
- packet.gtp0.h.length = hton16(length);
- packet.gtp0.h.flow = hton16(pdp->flrc);
- packet.gtp0.h.seq = ((union gtp_packet*)pack)->gtp0.h.seq;
- packet.gtp0.h.tid = (pdp->imsi & 0x0fffffffffffffff) + ((uint64_t)pdp->nsapi << 60);
+ if (version == 1)
+ gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE,
+ pdp->qos_neg.l, pdp->qos_neg.v);
+
+ /* TODO: Charging gateway address */
+ }
- return gtp_resp(0, gsn, &packet, GTP0_HEADER_SIZE+length, peer);
+ return gtp_resp(version, gsn, pdp, &packet, length, &pdp->sa_peer,
+ pdp->fd, pdp->seq, pdp->tid);
}
+
/* Handle Update PDP Context Request */
int gtp_update_pdp_ind(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, void *pack, unsigned len) {
- struct pdp_t *pdp, *pdp2;
- struct pdp_t pdp_buf;
+ struct sockaddr_in *peer, int fd,
+ void *pack, unsigned len) {
+ struct pdp_t *pdp;
+ struct pdp_t pdp_backup;
union gtpie_member* ie[GTPIE_SIZE];
uint8_t recovery;
- uint16_t seq = ntoh16(((union gtp_packet*)pack)->gtp0.h.seq);
+ uint16_t seq = get_seq(pack);
+ int hlen = get_hlen(pack);
+ uint64_t imsi;
+ uint8_t nsapi;
+
/* Is this a dublicate ? */
- if(!gtp_dublicate(gsn, 0, peer, seq)) {
+ if(!gtp_dublicate(gsn, version, peer, seq)) {
return 0; /* We allready send of response once */
}
- /* Find the pdp context in question */
- if (pdp_getgtp0(&pdp, ntoh16(((union gtp_packet*)pack)->gtp0.h.flow))) {
- gsn->err_unknownpdp++;
- gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
- "Unknown PDP context");
- return gtp_update_pdp_resp(gsn, version, peer, pack, len, NULL,
- GTPCAUSE_NON_EXIST);
- }
/* Decode information elements */
- if (gtpie_decaps(ie, pack+GTP0_HEADER_SIZE, len-GTP0_HEADER_SIZE)) {
+ if (gtpie_decaps(ie, version, pack+hlen, len-hlen)) {
gsn->invalid++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Invalid message format");
if (0 == version)
return EOF;
else
- return gtp_update_pdp_resp(gsn, version, peer, pack, len, pdp,
- GTPCAUSE_INVALID_MESSAGE);
+ return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len,
+ NULL, GTPCAUSE_INVALID_MESSAGE);
}
- pdp2 = &pdp_buf;
- memcpy(pdp2, pdp, sizeof (struct pdp_t)); /* Generate local copy */
+ /* Finding PDP: */
+ /* For GTP0 we use the tunnel identifier to provide imsi and nsapi. */
+ /* For GTP1 we must use imsi and nsapi if imsi is present. Otherwise */
+ /* we have to use the tunnel endpoint identifier */
+ if (version == 0) {
+ imsi = ((union gtp_packet*)pack)->gtp0.h.tid & 0x0fffffffffffffff;
+ nsapi = (((union gtp_packet*)pack)->gtp0.h.tid & 0xf000000000000000) >> 60;
- if (gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0, /* TODO: HACK only gtp0 */
- &pdp2->qos_req0, sizeof(pdp2->qos_req0))) {
- gsn->missing++;
- gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
- "Missing mandatory information field");
- return gtp_update_pdp_resp(gsn, version, peer, pack, len, pdp2,
- GTPCAUSE_MAN_IE_MISSING);
+ /* Find the context in question */
+ if (pdp_getimsi(&pdp, imsi, nsapi)) {
+ gsn->err_unknownpdp++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Unknown PDP context");
+ return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len,
+ NULL, GTPCAUSE_NON_EXIST);
+ }
}
- /* pdp2->qos_req.l = 3; * TODO: HACK only gtp0 */
+ else if (version == 1) {
+ /* NSAPI (mandatory) */
+ if (gtpie_gettv1(ie, GTPIE_NSAPI, 0, &nsapi)) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len,
+ NULL, GTPCAUSE_MAN_IE_MISSING);
+ }
- /* Extract recovery (optional) */
+ /* IMSI (conditional) */
+ if (gtpie_gettv0(ie, GTPIE_IMSI, 0, &imsi, sizeof(imsi))) {
+ /* Find the context in question */
+ if (pdp_getgtp1(&pdp, get_tei(pack))) {
+ gsn->err_unknownpdp++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Unknown PDP context");
+ return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len,
+ NULL, GTPCAUSE_NON_EXIST);
+ }
+ }
+ else {
+ /* Find the context in question */
+ if (pdp_getimsi(&pdp, imsi, nsapi)) {
+ gsn->err_unknownpdp++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Unknown PDP context");
+ return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len,
+ NULL, GTPCAUSE_NON_EXIST);
+ }
+ }
+ }
+ else {
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "Unknown version");
+ return EOF;
+ }
+
+ /* Make a backup copy in case anything is wrong */
+ memcpy(&pdp_backup, pdp, sizeof(pdp_backup));
+
+ if (version == 0) {
+ if (gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0,
+ pdp->qos_req0, sizeof(pdp->qos_req0))) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
+ return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len,
+ pdp, GTPCAUSE_MAN_IE_MISSING);
+ }
+ }
+
+ /* Recovery (optional) */
if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) {
/* TODO: Handle received recovery IE */
}
- if (gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp2->flru)) {
- gsn->missing++;
- gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
- "Missing mandatory information field");
- return gtp_update_pdp_resp(gsn, version, peer, pack, len, pdp2,
- GTPCAUSE_MAN_IE_MISSING);
+ if (version == 0) {
+ if (gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp->flru)) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
+ return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp,
+ GTPCAUSE_MAN_IE_MISSING);
+ }
+
+ if (gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp->flrc)) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
+ return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp,
+ GTPCAUSE_MAN_IE_MISSING);
+ }
}
- if (gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp2->flrc)) {
+
+ if (version == 1) {
+ /* TEID (mandatory) */
+ if (gtpie_gettv4(ie, GTPIE_TEI_DI, 0, &pdp->teid_gn)) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
+ return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp,
+ GTPCAUSE_MAN_IE_MISSING);
+ }
+
+ /* TEIC (conditional) */
+ /* If TEIC is not included it means that we have allready received it */
+ if (gtpie_gettv4(ie, GTPIE_TEI_C, 0, &pdp->teic_gn)) {
+ }
+
+ /* NSAPI (mandatory) */
+ if (gtpie_gettv1(ie, GTPIE_NSAPI, 0, &pdp->nsapi)) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
+ return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp,
+ GTPCAUSE_MAN_IE_MISSING);
+ }
+ }
+
+ /* Trace reference (optional) */
+ /* Trace type (optional) */
+
+ /* End User Address (conditional) TODO: GGSN Initiated
+ if (gtpie_gettlv(ie, GTPIE_EUA, 0, &pdp->eua.l,
+ &pdp->eua.v, sizeof(pdp->eua.v))) {
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing mandatory information field");
- return gtp_update_pdp_resp(gsn, version, peer, pack, len, pdp2,
+ memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
+ return gtp_update_pdp_resp(gsn, version, pdp,
GTPCAUSE_MAN_IE_MISSING);
- }
+ } */
- if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 0, &pdp2->gsnrc.l,
- &pdp2->gsnrc.v, sizeof(pdp2->gsnrc.v))) {
+
+ /* SGSN address for signalling (mandatory) */
+ /* It is weird that this is mandatory when TEIC is conditional */
+ if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 0, &pdp->gsnrc.l,
+ &pdp->gsnrc.v, sizeof(pdp->gsnrc.v))) {
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing mandatory information field");
- return gtp_update_pdp_resp(gsn, version, peer, pack, len, pdp2,
+ memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
+ return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp,
GTPCAUSE_MAN_IE_MISSING);
}
- if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 1, &pdp2->gsnru.l,
- &pdp2->gsnru.v, sizeof(pdp2->gsnru.v))) {
+ /* SGSN address for user traffic (mandatory) */
+ if (gtpie_gettlv(ie, GTPIE_GSN_ADDR, 1, &pdp->gsnru.l,
+ &pdp->gsnru.v, sizeof(pdp->gsnru.v))) {
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing mandatory information field");
- return gtp_update_pdp_resp(gsn, version, peer, pack, len, pdp2,
+ memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
+ return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp,
GTPCAUSE_MAN_IE_MISSING);
}
+
+ if (version == 1) {
+ /* QoS (mandatory) */
+ if (gtpie_gettlv(ie, GTPIE_QOS_PROFILE, 0, &pdp->qos_req.l,
+ &pdp->qos_req.v, sizeof(pdp->qos_req.v))) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ memcpy(pdp, &pdp_backup, sizeof(pdp_backup));
+ return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp,
+ GTPCAUSE_MAN_IE_MISSING);
+ }
- /* OK! It seames as if we received a valid message */
-
- memcpy(pdp, pdp2, sizeof (struct pdp_t)); /* Update original pdp */
+ /* TFT (conditional) */
+ if (gtpie_gettlv(ie, GTPIE_TFT, 0, &pdp->tft.l,
+ &pdp->tft.v, sizeof(pdp->tft.v))) {
+ }
+
+ /* OMC identity */
+ }
/* Confirm to peer that things were "successful" */
- return gtp_update_pdp_resp(gsn, version, peer, pack, len, pdp,
+ return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, pdp,
GTPCAUSE_ACC_REQ);
}
@@ -1494,27 +1882,27 @@ int gtp_update_pdp_conf(struct gsn_t *gsn, int version,
struct pdp_t *pdp;
union gtpie_member *ie[GTPIE_SIZE];
uint8_t cause, recovery;
- void *aid = NULL;
+ void *cbp = NULL;
uint8_t type = 0;
/* Remove packet from queue */
- if (gtp_conf(gsn, 0, peer, pack, len, &type, &aid)) return EOF;
+ if (gtp_conf(gsn, 0, peer, pack, len, &type, &cbp)) return EOF;
/* Find the context in question */
if (pdp_getgtp0(&pdp, ntoh16(((union gtp_packet*)pack)->gtp0.h.flow))) {
gsn->err_unknownpdp++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Unknown PDP context");
- if (gsn->cb_conf) gsn->cb_conf(type, cause, NULL, aid);
+ if (gsn->cb_conf) gsn->cb_conf(type, cause, NULL, cbp);
return EOF;
}
/* Decode information elements */
- if (gtpie_decaps(ie, pack+GTP0_HEADER_SIZE, len-GTP0_HEADER_SIZE)) {
+ if (gtpie_decaps(ie, 0, pack+GTP0_HEADER_SIZE, len-GTP0_HEADER_SIZE)) {
gsn->invalid++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Invalid message format");
- if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
if (gsn->cb_delete_context) gsn->cb_delete_context(pdp);
pdp_freepdp(pdp);
return EOF;
@@ -1525,7 +1913,7 @@ int gtp_update_pdp_conf(struct gsn_t *gsn, int version,
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing mandatory information field");
- if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
if (gsn->cb_delete_context) gsn->cb_delete_context(pdp);
pdp_freepdp(pdp);
return EOF;
@@ -1538,7 +1926,7 @@ int gtp_update_pdp_conf(struct gsn_t *gsn, int version,
/* Check all conditional information elements */
if (GTPCAUSE_ACC_REQ != cause) {
- if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, aid);
+ if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, cbp);
if (gsn->cb_delete_context) gsn->cb_delete_context(pdp);
pdp_freepdp(pdp);
return 0;
@@ -1556,7 +1944,7 @@ int gtp_update_pdp_conf(struct gsn_t *gsn, int version,
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing conditional information field");
- if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, aid);
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
if (gsn->cb_delete_context) gsn->cb_delete_context(pdp);
pdp_freepdp(pdp);
return EOF;
@@ -1576,55 +1964,51 @@ int gtp_update_pdp_conf(struct gsn_t *gsn, int version,
gtpie_gettlv(ie, GTPIE_GSN_ADDR, 1, &pdp->gsnru.l,
&pdp->gsnru.v, sizeof(pdp->gsnru.v));
- if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, aid);
+ if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, cbp);
return 0; /* Succes */
}
}
-/* Send Delete PDP Context Request */
-extern int gtp_delete_pdp_req(struct gsn_t *gsn, int version, void *aid,
- struct pdp_t *pdp) {
+
+/* API: Send Delete PDP Context Request */
+int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp) {
union gtp_packet packet;
- int length = 0;
+ int length = get_default_gtp(pdp->version, GTP_CREATE_PDP_REQ, &packet);
struct in_addr addr;
-
+
if (gsna2in_addr(&addr, &pdp->gsnrc)) {
gsn->err_address++;
gtp_err(LOG_ERR, __FILE__, __LINE__, "GSN address conversion failed");
return EOF;
}
+
+ if (pdp->version == 1) {
+ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI,
+ pdp->nsapi);
- get_default_gtp(0, &packet);
-
- packet.gtp0.h.type = hton8(GTP_DELETE_PDP_REQ);
- packet.gtp0.h.length = hton16(length);
- packet.gtp0.h.flow = hton16(pdp->flrc);
- packet.gtp0.h.tid = (pdp->imsi & 0x0fffffffffffffff) + ((uint64_t)pdp->nsapi << 60);
+ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_TEARDOWN,
+ 0xff);
+ }
- return gtp_req(gsn, 0, &packet, GTP0_HEADER_SIZE+length, &addr, aid);
+ get_default_gtp(pdp->version, GTP_DELETE_PDP_REQ, &packet);
+
+ return gtp_req(gsn, pdp->version, pdp, &packet, length, &addr, cbp);
}
+
/* Send Delete PDP Context Response */
int gtp_delete_pdp_resp(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer,
+ struct sockaddr_in *peer, int fd,
void *pack, unsigned len,
struct pdp_t *pdp, uint8_t cause)
{
union gtp_packet packet;
- int length = 0;
- uint16_t flow = 0;
-
- if (pdp) flow = hton16(pdp->flrc);
-
- get_default_gtp(0, &packet);
+ int length = get_default_gtp(version, GTP_DELETE_PDP_RSP, &packet);
- gtpie_tv1(packet.gtp0.p, &length, GTP_MAX, GTPIE_CAUSE, cause);
+ gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_CAUSE, cause);
- packet.gtp0.h.type = hton8(GTP_DELETE_PDP_RSP);
- packet.gtp0.h.length = hton16(length);
- packet.gtp0.h.flow = flow;
- packet.gtp0.h.seq = ((union gtp_packet*)pack)->gtp0.h.seq;
- packet.gtp0.h.tid = ((union gtp_packet*)pack)->gtp0.h.tid;
+ gtp_resp(version, gsn, pdp, &packet, length, peer, fd,
+ get_seq(pack),get_tid(pack));
if (pdp) {
/* Callback function to allow application to clean up */
@@ -1632,47 +2016,74 @@ int gtp_delete_pdp_resp(struct gsn_t *gsn, int version,
pdp_freepdp(pdp); /* Clean up PDP context */
}
- return gtp_resp(0, gsn, &packet, GTP0_HEADER_SIZE+length, peer);
+ return 0;
}
/* Handle Delete PDP Context Request */
int gtp_delete_pdp_ind(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer, void *pack, unsigned len) {
- struct pdp_t *pdp;
+ struct sockaddr_in *peer, int fd,
+ void *pack, unsigned len) {
+ struct pdp_t *pdp;
union gtpie_member* ie[GTPIE_SIZE];
- uint16_t seq = ntoh16(((union gtp_packet*)pack)->gtp0.h.seq);
+
+ uint16_t seq = get_seq(pack);
+ int hlen = get_hlen(pack);
+ uint8_t nsapi;
+ uint8_t teardown;
+
/* Is this a dublicate ? */
- if(!gtp_dublicate(gsn, 0, peer, seq)) {
- return 0;
+ if(!gtp_dublicate(gsn, version, peer, seq)) {
+ return 0; /* We allready send off response once */
}
- /* Find the pdp context in question */
- if (pdp_getgtp0(&pdp, ntoh16(((union gtp_packet*)pack)->gtp0.h.flow))) {
+ /* Find the context in question */
+ if (pdp_getgtp1(&pdp, get_tei(pack))) {
gsn->err_unknownpdp++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Unknown PDP context");
- if (0 == version)
- return gtp_delete_pdp_resp(gsn, version, peer, pack, len, NULL,
- GTPCAUSE_ACC_REQ);
- else
- return gtp_delete_pdp_resp(gsn, version, peer, pack, len, NULL,
- GTPCAUSE_NON_EXIST);
+ return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len,
+ NULL, GTPCAUSE_NON_EXIST);
}
/* Decode information elements */
- if (gtpie_decaps(ie, pack+GTP0_HEADER_SIZE, len-GTP0_HEADER_SIZE)) {
+ if (gtpie_decaps(ie, version, pack+hlen, len-hlen)) {
gsn->invalid++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Invalid message format");
if (0 == version)
return EOF;
else
- return gtp_delete_pdp_resp(gsn, version, peer, pack, len, pdp,
- GTPCAUSE_INVALID_MESSAGE);
+ return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len,
+ NULL, GTPCAUSE_INVALID_MESSAGE);
}
- return gtp_delete_pdp_resp(gsn, version, peer, pack, len, pdp,
+ if (version == 1) {
+ /* NSAPI (mandatory) */
+ if (gtpie_gettv1(ie, GTPIE_NSAPI, 0, &nsapi)) {
+ gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len,
+ NULL, GTPCAUSE_MAN_IE_MISSING);
+ }
+
+ /* TODO: When multiple contexts with the same IP address exists
+ we need to tear down each one individually or as a group depending
+ on the value of teardown */
+
+ /* Teardown (conditional) */
+ if (gtpie_gettv1(ie, GTPIE_TEARDOWN, 0, &teardown)) {
+ return 0; /* 29.060 7.3.5 Ignore message */
+ /* gsn->missing++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Missing mandatory information field");
+ return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len,
+ NULL, GTPCAUSE_MAN_IE_MISSING); */
+ }
+ }
+
+ return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len, pdp,
GTPCAUSE_ACC_REQ);
}
@@ -1684,33 +2095,37 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
struct pdp_t *pdp;
union gtpie_member *ie[GTPIE_SIZE];
uint8_t cause;
- void *aid = NULL;
+ void *cbp = NULL;
uint8_t type = 0;
-
+ int hlen = get_hlen(pack);
+
/* Remove packet from queue */
- if (gtp_conf(gsn, 0, peer, pack, len, &type, &aid)) return EOF;
+ if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp)) return EOF;
/* Find the context in question */
- if (pdp_getgtp0(&pdp, ntoh16(((union gtp_packet*)pack)->gtp0.h.flow))) {
+ if (pdp_getgtp1(&pdp, get_tei(pack))) {
gsn->err_unknownpdp++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Unknown PDP context");
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, NULL, cbp);
return EOF;
}
/* Decode information elements */
- if (gtpie_decaps(ie, pack+GTP0_HEADER_SIZE, len-GTP0_HEADER_SIZE)) {
+ if (gtpie_decaps(ie, version, pack+hlen, len-hlen)) {
gsn->invalid++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Invalid message format");
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
return EOF;
}
- /* Extract cause value */
+ /* Extract cause value (mandatory) */
if (gtpie_gettv1(ie, GTPIE_CAUSE, 0, &cause)) {
gsn->missing++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Missing mandatory information field");
+ if (gsn->cb_conf) gsn->cb_conf(type, EOF, pdp, cbp);
return EOF;
}
@@ -1719,12 +2134,12 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
gsn->err_cause++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Unexpected cause value received: %d", cause);
- return EOF;
}
- /* Callback function to allow application to clean up */
- if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, aid);
+ /* Callback function to notify application */
+ if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, cbp);
+ /* Callback function to allow application to clean up */
if (gsn->cb_delete_context) gsn->cb_delete_context(pdp);
pdp_freepdp(pdp);
@@ -1733,21 +2148,14 @@ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version,
/* Send Error Indication (response to a GPDU message */
int gtp_error_ind_resp(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer,
+ struct sockaddr_in *peer, int fd,
void *pack, unsigned len)
{
union gtp_packet packet;
- int length = 0;
-
- get_default_gtp(0, &packet);
+ int length = get_default_gtp(version, GTP_ERROR, &packet);
- packet.gtp0.h.type = hton8(GTP_ERROR);
- packet.gtp0.h.length = hton16(length);
- packet.gtp0.h.flow = 0;
- packet.gtp0.h.seq = ((union gtp_packet*)pack)->gtp0.h.seq;
- packet.gtp0.h.tid = ((union gtp_packet*)pack)->gtp0.h.tid;
-
- return gtp_resp(0, gsn, &packet, GTP0_HEADER_SIZE+length, peer);
+ return gtp_resp(version, gsn, NULL, &packet, length, peer, fd,
+ get_seq(pack), get_tid(pack));
}
/* Handle Error Indication */
@@ -1774,78 +2182,93 @@ int gtp_error_ind_conf(struct gsn_t *gsn, int version,
}
int gtp_gpdu_ind(struct gsn_t *gsn, int version,
- struct sockaddr_in *peer,
- void *pack,
- unsigned len) {
+ struct sockaddr_in *peer, int fd,
+ void *pack, unsigned len) {
+
+ int hlen = GTP1_HEADER_SIZE_SHORT;
/* Need to include code to verify packet src and dest addresses */
struct pdp_t *pdp;
-
- if (pdp_getgtp0(&pdp, ntoh16(((union gtp_packet*)pack)->gtp0.h.flow))) {
- gsn->err_unknownpdp++;
- gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
- "Unknown PDP context");
- return gtp_error_ind_resp(gsn, version, peer, pack, len);
-
+
+ if (version == 0) {
+ if (pdp_getgtp0(&pdp, ntoh16(((union gtp_packet*)pack)->gtp0.h.flow))) {
+ gsn->err_unknownpdp++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Unknown PDP context");
+ return gtp_error_ind_resp(gsn, version, peer, fd, pack, len);
+ }
+ hlen = GTP0_HEADER_SIZE;
}
+ else if (version == 1) {
+ if (pdp_getgtp1(&pdp, ntoh32(((union gtp_packet*)pack)->gtp1l.h.tei))) {
+ gsn->err_unknownpdp++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Unknown PDP context");
+ return gtp_error_ind_resp(gsn, version, peer, fd, pack, len);
+ }
+ /* Is this a long or a short header ? */
+ if (((union gtp_packet*)pack)->gtp1l.h.flags & 0x07)
+ hlen = GTP1_HEADER_SIZE_LONG;
+ else
+ hlen = GTP1_HEADER_SIZE_SHORT;
+ }
+ else {
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
+ "Unknown version");
+ }
+
/* If the GPDU was not from the peer GSN tell him to delete context */
if (memcmp(&peer->sin_addr, pdp->gsnru.v, pdp->gsnru.l)) { /* TODO Range? */
gsn->err_unknownpdp++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len,
"Unknown PDP context");
- return gtp_error_ind_resp(gsn, version, peer, pack, len);
+ return gtp_error_ind_resp(gsn, version, peer, fd, pack, len);
}
-
+
/* Callback function */
- if (gsn->cb_gpdu !=0)
- return gsn->cb_gpdu(pdp, pack+20, len-20); /* TODO ???? */
+ if (gsn->cb_data_ind !=0)
+ return gsn->cb_data_ind(pdp, pack+hlen, len-hlen);
return 0;
}
+
/* Receives GTP packet and sends off for further processing
* Function will check the validity of the header. If the header
* is not valid the packet is either dropped or a version not
* supported is returned to the peer.
* TODO: Need to decide on return values! */
-int gtp_decaps(struct gsn_t *gsn)
+int gtp_decaps0(struct gsn_t *gsn)
{
unsigned char buffer[PACKET_MAX + 64 /*TODO: ip header */ ];
- int status, ip_len = 0;
struct sockaddr_in peer;
int peerlen;
+ int status;
struct gtp0_header *pheader;
int version = 0; /* GTP version should be determined from header!*/
+ int fd = gsn->fd0;
/* TODO: Need strategy of userspace buffering and blocking */
/* Currently read is non-blocking and send is blocking. */
/* This means that the program have to wait for busy send calls...*/
while (1) { /* Loop until no more to read */
- if (fcntl(gsn->fd, F_SETFL, O_NONBLOCK)) {
+ if (fcntl(gsn->fd0, F_SETFL, O_NONBLOCK)) {
gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()");
return -1;
}
peerlen = sizeof(peer);
if ((status =
- recvfrom(gsn->fd, buffer, sizeof(buffer), 0,
+ recvfrom(gsn->fd0, buffer, sizeof(buffer), 0,
(struct sockaddr *) &peer, &peerlen)) < 0 ) {
- if (errno == EAGAIN) return -1;
+ if (errno == EAGAIN) return 0;
gsn->err_readfrom++;
- gtp_err(LOG_ERR, __FILE__, __LINE__, "recvfrom(fd=%d, buffer=%lx, len=%d) failed: status = %d error = %s", gsn->fd, (unsigned long) buffer, sizeof(buffer), status, status ? strerror(errno) : "No error");
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "recvfrom(fd0=%d, buffer=%lx, len=%d) failed: status = %d error = %s", gsn->fd0, (unsigned long) buffer, sizeof(buffer), status, status ? strerror(errno) : "No error");
return -1;
}
- /* Strip off IP header, if present: TODO Is this nessesary? */
- if ((buffer[0] & 0xF0) == 0x40) {
- ip_len = (buffer[0] & 0xF) * 4;
- gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
- "IP header found in return from read");
- continue;
- }
-
/* Need at least 1 byte in order to check version */
if (status < (1)) {
gsn->empty++;
@@ -1854,30 +2277,37 @@ int gtp_decaps(struct gsn_t *gsn)
continue;
}
- /* TODO: Remove these ERROR MESSAGES
- gtp_err(LOG_ERR, __FILE__, __LINE__, "Discarding packet - too small");
- gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
- "Discarding packet - too small"); */
-
- pheader = (struct gtp0_header *) (buffer + ip_len);
+ pheader = (struct gtp0_header *) (buffer);
- /* Version should be gtp0 (or earlier in theory) */
+ /* Version should be gtp0 (or earlier) */
+ /* 09.60 is somewhat unclear on this issue. On gsn->fd0 we expect only */
+ /* GTP 0 messages. If other version message is received we reply that we */
+ /* only support version 0, implying that this is the only version */
+ /* supported on this port */
if (((pheader->flags & 0xe0) > 0x00)) {
gsn->unsup++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
"Unsupported GTP version");
- gtp_unsup_resp(gsn, &peer, buffer, status); /* 29.60: 11.1.1 */
+ gtp_unsup_req(gsn, 0, &peer, gsn->fd0, buffer, status); /* 29.60: 11.1.1 */
continue;
}
/* Check length of gtp0 packet */
- if (((pheader->flags & 0xe0) == 0x00) && (status < GTP0_HEADER_SIZE)) {
+ if (status < GTP0_HEADER_SIZE) {
gsn->tooshort++;
gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
"GTP0 packet too short");
continue; /* Silently discard 29.60: 11.1.2 */
}
+ /* Check packet length field versus length of packet */
+ if (status != (ntoh16(pheader->length) + GTP0_HEADER_SIZE)) {
+ gsn->tooshort++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "GTP packet length field does not match actual length");
+ continue; /* Silently discard */
+ }
+
if ((gsn->mode == GTP_MODE_GGSN) &&
((pheader->type == GTP_CREATE_PDP_RSP) ||
(pheader->type == GTP_UPDATE_PDP_RSP) ||
@@ -1898,48 +2328,289 @@ int gtp_decaps(struct gsn_t *gsn)
continue; /* Silently discard 29.60: 11.1.4 */
}
+ switch (pheader->type) {
+ case GTP_ECHO_REQ:
+ gtp_echo_ind(gsn, version, &peer, fd, buffer, status);
+ break;
+ case GTP_ECHO_RSP:
+ gtp_echo_conf(gsn, version, &peer, buffer, status);
+ break;
+ case GTP_NOT_SUPPORTED:
+ gtp_unsup_ind(gsn, &peer, buffer, status);
+ break;
+ case GTP_CREATE_PDP_REQ:
+ gtp_create_pdp_ind(gsn, version, &peer, fd, buffer, status);
+ break;
+ case GTP_CREATE_PDP_RSP:
+ gtp_create_pdp_conf(gsn, version, &peer, buffer, status);
+ break;
+ case GTP_UPDATE_PDP_REQ:
+ gtp_update_pdp_ind(gsn, version, &peer, fd, buffer, status);
+ break;
+ case GTP_UPDATE_PDP_RSP:
+ gtp_update_pdp_conf(gsn, version, &peer, buffer, status);
+ break;
+ case GTP_DELETE_PDP_REQ:
+ gtp_delete_pdp_ind(gsn, version, &peer, fd, buffer, status);
+ break;
+ case GTP_DELETE_PDP_RSP:
+ gtp_delete_pdp_conf(gsn, version, &peer, buffer, status);
+ break;
+ case GTP_ERROR:
+ gtp_error_ind_conf(gsn, version, &peer, buffer, status);
+ break;
+ case GTP_GPDU:
+ gtp_gpdu_ind(gsn, version, &peer, fd, buffer, status);
+ break;
+ default:
+ gsn->unknown++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "Unknown GTP message type received");
+ break;
+ }
+ }
+}
+
+int gtp_decaps1c(struct gsn_t *gsn)
+{
+ unsigned char buffer[PACKET_MAX + 64 /*TODO: ip header */ ];
+ struct sockaddr_in peer;
+ int peerlen;
+ int status;
+ struct gtp1_header_short *pheader;
+ int version = 1; /* GTP version should be determined from header!*/
+ int fd = gsn->fd1c;
+
+ /* TODO: Need strategy of userspace buffering and blocking */
+ /* Currently read is non-blocking and send is blocking. */
+ /* This means that the program have to wait for busy send calls...*/
+
+ while (1) { /* Loop until no more to read */
+ if (fcntl(gsn->fd1c, F_SETFL, O_NONBLOCK)) {
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()");
+ return -1;
+ }
+ peerlen = sizeof(peer);
+ if ((status =
+ recvfrom(gsn->fd1c, buffer, sizeof(buffer), 0,
+ (struct sockaddr *) &peer, &peerlen)) < 0 ) {
+ if (errno == EAGAIN) return 0;
+ gsn->err_readfrom++;
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "recvfrom(fd1c=%d, buffer=%lx, len=%d) failed: status = %d error = %s", gsn->fd1c, (unsigned long) buffer, sizeof(buffer), status, status ? strerror(errno) : "No error");
+ return -1;
+ }
+
+ /* Need at least 1 byte in order to check version */
+ if (status < (1)) {
+ gsn->empty++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "Discarding packet - too small");
+ continue;
+ }
+
+ pheader = (struct gtp1_header_short *) (buffer);
+ /* Version must be no larger than GTP 1 */
+ if (((pheader->flags & 0xe0) > 0x20)) {
+ gsn->unsup++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "Unsupported GTP version");
+ gtp_unsup_req(gsn, 1, &peer, gsn->fd1c, buffer, status);/*29.60: 11.1.1*/
+ continue;
+ }
+
+ /* Version must be at least GTP 1 */
+ /* 29.060 is somewhat unclear on this issue. On gsn->fd1c we expect only */
+ /* GTP 1 messages. If GTP 0 message is received we silently discard */
+ /* the message */
+ if (((pheader->flags & 0xe0) < 0x20)) {
+ gsn->unsup++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "Unsupported GTP version");
+ continue;
+ }
+
+ /* Check packet flag field */
+ if (((pheader->flags & 0xf7) != 0x32)) {
+ gsn->unsup++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "Unsupported packet flag");
+ continue;
+ }
+
+ /* Check length of packet */
+ if (status < GTP1_HEADER_SIZE_LONG) {
+ gsn->tooshort++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "GTP packet too short");
+ continue; /* Silently discard 29.60: 11.1.2 */
+ }
+
+ /* Check packet length field versus length of packet */
+ if (status != (ntoh16(pheader->length) + GTP1_HEADER_SIZE_SHORT)) {
+ gsn->tooshort++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "GTP packet length field does not match actual length");
+ continue; /* Silently discard */
+ }
+
+ if ((gsn->mode == GTP_MODE_GGSN) &&
+ ((pheader->type == GTP_CREATE_PDP_RSP) ||
+ (pheader->type == GTP_UPDATE_PDP_RSP) ||
+ (pheader->type == GTP_DELETE_PDP_RSP))) {
+ gsn->unexpect++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "Unexpected GTP Signalling Message");
+ continue; /* Silently discard 29.60: 11.1.4 */
+ }
+
+ if ((gsn->mode == GTP_MODE_SGSN) &&
+ ((pheader->type == GTP_CREATE_PDP_REQ) ||
+ (pheader->type == GTP_UPDATE_PDP_REQ) ||
+ (pheader->type == GTP_DELETE_PDP_REQ))) {
+ gsn->unexpect++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "Unexpected GTP Signalling Message");
+ continue; /* Silently discard 29.60: 11.1.4 */
+ }
+
switch (pheader->type) {
case GTP_ECHO_REQ:
- gtp_echo_ind(gsn, &peer, buffer+ip_len, status - ip_len);
+ gtp_echo_ind(gsn, version, &peer, fd, buffer, status);
break;
case GTP_ECHO_RSP:
- gtp_echo_conf(gsn, &peer, buffer+ip_len, status - ip_len);
+ gtp_echo_conf(gsn, version, &peer, buffer, status);
break;
case GTP_NOT_SUPPORTED:
- gtp_unsup_conf(gsn, &peer, buffer+ip_len, status - ip_len);
+ gtp_unsup_ind(gsn, &peer, buffer, status);
break;
case GTP_CREATE_PDP_REQ:
- gtp_create_pdp_ind(gsn, version, &peer, buffer+ip_len,
- status - ip_len);
+ gtp_create_pdp_ind(gsn, version, &peer, fd, buffer, status);
break;
case GTP_CREATE_PDP_RSP:
- gtp_create_pdp_conf(gsn, version, &peer, buffer+ip_len,
- status - ip_len);
+ gtp_create_pdp_conf(gsn, version, &peer, buffer, status);
break;
case GTP_UPDATE_PDP_REQ:
- gtp_update_pdp_ind(gsn, version, &peer, buffer+ip_len,
- status - ip_len);
+ gtp_update_pdp_ind(gsn, version, &peer, fd, buffer, status);
break;
case GTP_UPDATE_PDP_RSP:
- gtp_update_pdp_conf(gsn, version, &peer, buffer+ip_len,
- status - ip_len);
+ gtp_update_pdp_conf(gsn, version, &peer, buffer, status);
break;
case GTP_DELETE_PDP_REQ:
- gtp_delete_pdp_ind(gsn, version, &peer, buffer+ip_len,
- status - ip_len);
+ gtp_delete_pdp_ind(gsn, version, &peer, fd, buffer, status);
break;
case GTP_DELETE_PDP_RSP:
- gtp_delete_pdp_conf(gsn, version, &peer, buffer+ip_len,
- status - ip_len);
+ gtp_delete_pdp_conf(gsn, version, &peer, buffer, status);
break;
case GTP_ERROR:
- gtp_error_ind_conf(gsn, version, &peer, buffer+ip_len,
- status - ip_len);
+ gtp_error_ind_conf(gsn, version, &peer, buffer, status);
+ break;
+ default:
+ gsn->unknown++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "Unknown GTP message type received");
break;
+ }
+ }
+}
+
+int gtp_decaps1u(struct gsn_t *gsn)
+{
+ unsigned char buffer[PACKET_MAX + 64 /*TODO: ip header */ ];
+ struct sockaddr_in peer;
+ int peerlen;
+ int status;
+ struct gtp1_header_short *pheader;
+ int version = 1; /* GTP version should be determined from header!*/
+ int fd = gsn->fd1u;
+
+ /* TODO: Need strategy of userspace buffering and blocking */
+ /* Currently read is non-blocking and send is blocking. */
+ /* This means that the program have to wait for busy send calls...*/
+
+ while (1) { /* Loop until no more to read */
+ if (fcntl(gsn->fd1u, F_SETFL, O_NONBLOCK)) {
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()");
+ return -1;
+ }
+ peerlen = sizeof(peer);
+ if ((status =
+ recvfrom(gsn->fd1u, buffer, sizeof(buffer), 0,
+ (struct sockaddr *) &peer, &peerlen)) < 0 ) {
+ if (errno == EAGAIN) return 0;
+ gsn->err_readfrom++;
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "recvfrom(fd1u=%d, buffer=%lx, len=%d) failed: status = %d error = %s", gsn->fd1u, (unsigned long) buffer, sizeof(buffer), status, status ? strerror(errno) : "No error");
+ return -1;
+ }
+
+ /* Need at least 1 byte in order to check version */
+ if (status < (1)) {
+ gsn->empty++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "Discarding packet - too small");
+ continue;
+ }
+
+ pheader = (struct gtp1_header_short *) (buffer);
+
+ /* Version must be no larger than GTP 1 */
+ if (((pheader->flags & 0xe0) > 0x20)) {
+ gsn->unsup++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "Unsupported GTP version");
+ gtp_unsup_req(gsn, 1, &peer, gsn->fd1c, buffer, status);/*29.60: 11.1.1*/
+ continue;
+ }
+
+ /* Version must be at least GTP 1 */
+ /* 29.060 is somewhat unclear on this issue. On gsn->fd1c we expect only */
+ /* GTP 1 messages. If GTP 0 message is received we silently discard */
+ /* the message */
+ if (((pheader->flags & 0xe0) < 0x20)) {
+ gsn->unsup++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "Unsupported GTP version");
+ continue;
+ }
+
+ /* Check packet flag field (allow both with and without sequence number)*/
+ if (((pheader->flags & 0xf5) != 0x30)) {
+ gsn->unsup++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "Unsupported packet flag");
+ continue;
+ }
+
+ /* Check length of packet */
+ if (status < GTP1_HEADER_SIZE_SHORT) {
+ gsn->tooshort++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "GTP packet too short");
+ continue; /* Silently discard 29.60: 11.1.2 */
+ }
+
+ /* Check packet length field versus length of packet */
+ if (status != (ntoh16(pheader->length) + GTP1_HEADER_SIZE_SHORT)) {
+ gsn->tooshort++;
+ gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status,
+ "GTP packet length field does not match actual length");
+ continue; /* Silently discard */
+ }
+
+ switch (pheader->type) {
+ case GTP_ECHO_REQ:
+ gtp_echo_ind(gsn, version, &peer, fd, buffer, status);
+ break;
+ case GTP_ECHO_RSP:
+ gtp_echo_conf(gsn, version, &peer, buffer, status);
+ break;
+ case GTP_ERROR:
+ gtp_error_ind_conf(gsn, version, &peer, buffer, status);
+ break;
+ /* Supported header extensions */
case GTP_GPDU:
- gtp_gpdu_ind(gsn, version, &peer, buffer+ip_len, status - ip_len);
+ gtp_gpdu_ind(gsn, version, &peer, fd, buffer, status);
break;
default:
gsn->unknown++;
@@ -1950,46 +2621,72 @@ int gtp_decaps(struct gsn_t *gsn)
}
}
-int gtp_gpdu(struct gsn_t *gsn, struct pdp_t* pdp,
+int gtp_data_req(struct gsn_t *gsn, struct pdp_t* pdp,
void *pack, unsigned len)
{
union gtp_packet packet;
struct sockaddr_in addr;
-
- /*printf("gtp_encaps start\n");
- print_packet(pack, len);*/
+ int fd;
+ int length;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
-
memcpy(&addr.sin_addr, pdp->gsnru.v,pdp->gsnru.l); /* TODO range check */
- addr.sin_port = htons(GTP0_PORT);
- get_default_gtp(0, &packet);
- packet.gtp0.h.type = hton8(GTP_GPDU);
- packet.gtp0.h.length = hton16(len);
- packet.gtp0.h.seq = hton16(pdp->gtpsntx++);
- packet.gtp0.h.flow = hton16(pdp->flru);
- packet.gtp0.h.tid = (pdp->imsi & 0x0fffffffffffffff) + ((uint64_t)pdp->nsapi << 60);
-
- if (len > sizeof (union gtp_packet) - sizeof(struct gtp0_header)) {
- gsn->err_memcpy++;
+ if (pdp->version == 0) {
+
+ length = GTP0_HEADER_SIZE+len;
+ addr.sin_port = htons(GTP0_PORT);
+ fd = gsn->fd0;
+
+ get_default_gtp(0, GTP_GPDU, &packet);
+ packet.gtp0.h.length = hton16(len);
+ packet.gtp0.h.seq = hton16(pdp->gtpsntx++);
+ packet.gtp0.h.flow = hton16(pdp->flru);
+ packet.gtp0.h.tid = (pdp->imsi & 0x0fffffffffffffff) + ((uint64_t)pdp->nsapi << 60);
+
+ if (len > sizeof (union gtp_packet) - sizeof(struct gtp0_header)) {
+ gsn->err_memcpy++;
+ gtp_err(LOG_ERR, __FILE__, __LINE__,
+ "Memcpy failed");
+ return EOF;
+ }
+ memcpy(packet.gtp0.p, pack, len); /* TODO Should be avoided! */
+ }
+ else if (pdp->version == 1) {
+
+ length = GTP1_HEADER_SIZE_LONG+len;
+ addr.sin_port = htons(GTP1U_PORT);
+ fd = gsn->fd1u;
+
+ get_default_gtp(1, GTP_GPDU, &packet);
+ packet.gtp1l.h.length = hton16(len-GTP1_HEADER_SIZE_SHORT+
+ GTP1_HEADER_SIZE_LONG);
+ packet.gtp1l.h.seq = hton16(pdp->gtpsntx++);
+
+ if (len > sizeof (union gtp_packet) - sizeof(struct gtp1_header_long)) {
+ gsn->err_memcpy++;
+ gtp_err(LOG_ERR, __FILE__, __LINE__,
+ "Memcpy failed");
+ return EOF;
+ }
+ memcpy(packet.gtp1l.p, pack, len); /* TODO Should be avoided! */
+ }
+ else {
gtp_err(LOG_ERR, __FILE__, __LINE__,
- "Memcpy failed");
+ "Unknown version");
return EOF;
- }
+ }
- if (fcntl(gsn->fd, F_SETFL, 0)) {
+ if (fcntl(fd, F_SETFL, 0)) {
gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()");
return -1;
}
- memcpy(packet.gtp0.p, pack, len); /* TODO Should be avoided! */
-
- if (sendto(gsn->fd, &packet, GTP0_HEADER_SIZE+len, 0,
+ if (sendto(fd, &packet, length, 0,
(struct sockaddr *) &addr, sizeof(addr)) < 0) {
gsn->err_sendto++;
- gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", gsn->fd, (unsigned long) &packet, GTP0_HEADER_SIZE+len, strerror(errno));
+ gtp_err(LOG_ERR, __FILE__, __LINE__, "Sendto(fd=%d, msg=%lx, len=%d) failed: Error = %s", fd, (unsigned long) &packet, GTP0_HEADER_SIZE+len, strerror(errno));
return EOF;
}
return 0;