diff options
Diffstat (limited to 'gtp/gtp.c')
-rw-r--r-- | gtp/gtp.c | 5425 |
1 files changed, 2804 insertions, 2621 deletions
@@ -17,7 +17,6 @@ * - Do we need to handle fragmentation? */ - #ifdef __linux__ #define _GNU_SOURCE 1 #endif @@ -52,128 +51,127 @@ #include "gtpie.h" #include "queue.h" - /* Error reporting functions */ -void gtp_err(int priority, char *filename, int linenum, char *fmt, ...) { - va_list args; - char buf[ERRMSG_SIZE]; - - va_start(args, fmt); - vsnprintf(buf, ERRMSG_SIZE, fmt, args); - va_end(args); - buf[ERRMSG_SIZE-1] = 0; - syslog(priority, "%s: %d: %s", filename, linenum, buf); +void gtp_err(int priority, char *filename, int linenum, char *fmt, ...) +{ + va_list args; + char buf[ERRMSG_SIZE]; + + va_start(args, fmt); + vsnprintf(buf, ERRMSG_SIZE, fmt, args); + va_end(args); + buf[ERRMSG_SIZE - 1] = 0; + syslog(priority, "%s: %d: %s", filename, linenum, buf); } void gtp_errpack(int pri, char *fn, int ln, struct sockaddr_in *peer, - void *pack, unsigned len, char *fmt, ...) { - - va_list args; - char buf[ERRMSG_SIZE]; - char buf2[ERRMSG_SIZE]; - unsigned int n; - int pos; - - va_start(args, fmt); - vsnprintf(buf, ERRMSG_SIZE, fmt, args); - va_end(args); - buf[ERRMSG_SIZE-1] = 0; - - snprintf(buf2, ERRMSG_SIZE, "Packet from %s:%u, length: %d, content:", - inet_ntoa(peer->sin_addr), - ntohs(peer->sin_port), - len); - buf2[ERRMSG_SIZE-1] = 0; - pos = strlen(buf2); - for(n=0; n<len; n++) { - if ((pos+4)<ERRMSG_SIZE) { - sprintf((buf2+pos), " %02hhx", ((unsigned char*)pack)[n]); - pos += 3; - } - } - buf2[pos] = 0; - - syslog(pri, "%s: %d: %s. %s", fn, ln, buf, buf2); - -} + void *pack, unsigned len, char *fmt, ...) +{ + va_list args; + char buf[ERRMSG_SIZE]; + char buf2[ERRMSG_SIZE]; + unsigned int n; + int pos; + + va_start(args, fmt); + vsnprintf(buf, ERRMSG_SIZE, fmt, args); + va_end(args); + buf[ERRMSG_SIZE - 1] = 0; + + snprintf(buf2, ERRMSG_SIZE, "Packet from %s:%u, length: %d, content:", + inet_ntoa(peer->sin_addr), ntohs(peer->sin_port), len); + buf2[ERRMSG_SIZE - 1] = 0; + pos = strlen(buf2); + for (n = 0; n < len; n++) { + if ((pos + 4) < ERRMSG_SIZE) { + sprintf((buf2 + pos), " %02hhx", + ((unsigned char *)pack)[n]); + pos += 3; + } + } + buf2[pos] = 0; + syslog(pri, "%s: %d: %s. %s", fn, ln, buf, buf2); +} /* API Functions */ -const char* gtp_version() +const char *gtp_version() { - return VERSION; + return VERSION; } /* gtp_new */ /* gtp_free */ -int gtp_newpdp(struct gsn_t* gsn, struct pdp_t **pdp, - uint64_t imsi, uint8_t nsapi) { - return pdp_newpdp(pdp, imsi, nsapi, NULL); +int gtp_newpdp(struct gsn_t *gsn, struct pdp_t **pdp, + uint64_t imsi, uint8_t nsapi) +{ + return pdp_newpdp(pdp, imsi, nsapi, NULL); } -int gtp_freepdp(struct gsn_t* gsn, struct pdp_t *pdp) { - return pdp_freepdp(pdp); +int gtp_freepdp(struct gsn_t *gsn, struct pdp_t *pdp) +{ + return pdp_freepdp(pdp); } /* gtp_gpdu */ -extern int gtp_fd(struct gsn_t *gsn) { - return gsn->fd0; +extern int gtp_fd(struct gsn_t *gsn) +{ + return gsn->fd0; } /* gtp_decaps */ /* gtp_retrans */ /* gtp_retranstimeout */ - int gtp_set_cb_unsup_ind(struct gsn_t *gsn, - int (*cb) (struct sockaddr_in *peer)) { - gsn->cb_unsup_ind = cb; - return 0; + int (*cb) (struct sockaddr_in * peer)) +{ + gsn->cb_unsup_ind = cb; + return 0; } int gtp_set_cb_extheader_ind(struct gsn_t *gsn, - int (*cb) (struct sockaddr_in *peer)) { - gsn->cb_extheader_ind = cb; - return 0; + int (*cb) (struct sockaddr_in * peer)) +{ + gsn->cb_extheader_ind = cb; + return 0; } - /* 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)) +int gtp_set_cb_delete_context(struct gsn_t *gsn, int (*cb) (struct pdp_t * pdp)) { - gsn->cb_delete_context = cb; - return 0; + gsn->cb_delete_context = cb; + return 0; } int gtp_set_cb_conf(struct gsn_t *gsn, - int (*cb) (int type, int cause, - struct pdp_t* pdp, void *cbp)) { - gsn->cb_conf = cb; - return 0; + int (*cb) (int type, int cause, + struct pdp_t * pdp, void *cbp)) +{ + gsn->cb_conf = cb; + return 0; } int gtp_set_cb_recovery(struct gsn_t *gsn, - int (*cb) (struct sockaddr_in *peer, - uint8_t recovery)) { - gsn->cb_recovery = cb; - return 0; + int (*cb) (struct sockaddr_in * peer, uint8_t recovery)) +{ + gsn->cb_recovery = cb; + return 0; } extern int gtp_set_cb_data_ind(struct gsn_t *gsn, - int (*cb_data_ind) (struct pdp_t* pdp, - void* pack, - unsigned len)) + int (*cb_data_ind) (struct pdp_t * pdp, + void *pack, unsigned len)) { - gsn->cb_data_ind = cb_data_ind; - return 0; + gsn->cb_data_ind = cb_data_ind; + return 0; } /** @@ -184,35 +182,38 @@ extern int gtp_set_cb_data_ind(struct gsn_t *gsn, * to hold the packet header. * returns the length of the header. 0 on error. **/ -static unsigned int get_default_gtp(int version, uint8_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(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; - return GTP0_HEADER_SIZE; - case 1: - /* Initialise "standard" GTP1 header */ - /* 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; - } +static unsigned int get_default_gtp(int version, uint8_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(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; + return GTP0_HEADER_SIZE; + case 1: + /* Initialise "standard" GTP1 header */ + /* 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; + } } /** @@ -220,18 +221,18 @@ static unsigned int get_default_gtp(int version, uint8_t type, void *packet) { * 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; - } +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; + } } /** @@ -239,13 +240,14 @@ static uint16_t get_seq(void *pack) { * 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; +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; } /** @@ -253,21 +255,20 @@ static uint64_t get_tid(void *pack) { * 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; - } +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; + } } /** @@ -275,55 +276,53 @@ static uint16_t get_hlen(void *pack) { * 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; - } +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) { - unsigned int i; - printf("The packet looks like this (%d bytes):\n", len); - for( i=0; i<len; i++) { - printf("%02x ", (unsigned char)*(char *)(packet+i)); - if (!((i+1)%16)) printf("\n"); - }; - printf("\n"); - return 0; + unsigned int i; + printf("The packet looks like this (%d bytes):\n", len); + for (i = 0; i < len; i++) { + printf("%02x ", (unsigned char)*(char *)(packet + i)); + if (!((i + 1) % 16)) + printf("\n"); + }; + printf("\n"); + return 0; } -char* snprint_packet(struct gsn_t *gsn, struct sockaddr_in *peer, - void *pack, unsigned len, char *buf, int size) { - unsigned int n; - int pos; - snprintf(buf, size, "Packet from %s:%u, length: %d, content:", - inet_ntoa(peer->sin_addr), - ntohs(peer->sin_port), - len); - buf[size-1] = 0; - pos = strlen(buf); - for(n=0; n<len; n++) { - if ((pos+4)<size) { - sprintf((buf+pos), " %02hhx", ((unsigned char*)pack)[n]); - pos += 3; - } - } - buf[pos] = 0; - return buf; +char *snprint_packet(struct gsn_t *gsn, struct sockaddr_in *peer, + void *pack, unsigned len, char *buf, int size) +{ + unsigned int n; + int pos; + snprintf(buf, size, "Packet from %s:%u, length: %d, content:", + inet_ntoa(peer->sin_addr), ntohs(peer->sin_port), len); + buf[size - 1] = 0; + pos = strlen(buf); + for (n = 0; n < len; n++) { + if ((pos + 4) < size) { + sprintf((buf + pos), " %02hhx", + ((unsigned char *)pack)[n]); + pos += 3; + } + } + buf[pos] = 0; + return buf; } - /* *********************************************************** * Reliable delivery of signalling messages * @@ -403,71 +402,74 @@ char* snprint_packet(struct gsn_t *gsn, struct sockaddr_in *peer, *************************************************************/ 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; + 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; #if defined(__FreeBSD__) || defined(__APPLE__) - addr.sin_len = sizeof(addr); + addr.sin_len = sizeof(addr); #endif - 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 & 0x0fffffffffffffffull) + - ((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(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", fd, (unsigned long) &packet, len, strerror(errno)); - return -1; - } - - /* Use new queue structure */ - if (queue_newmsg(gsn->queue_req, &qmsg, &addr, gsn->seq_next)) { - gsn->err_queuefull++; - gtp_err(LOG_ERR, __FILE__, __LINE__, "Retransmit queue is full"); - } - else { - memcpy(&qmsg->p, packet, sizeof(union gtp_packet)); - qmsg->l = len; - qmsg->timeout = time(NULL) + 3; /* When to timeout */ - qmsg->retrans = 0; /* No retransmissions so far */ - qmsg->cbp = cbp; - qmsg->type = ntoh8(packet->gtp0.h.type); - qmsg->fd = fd; - } - gsn->seq_next++; /* Count up this time */ - return 0; + 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 & 0x0fffffffffffffffull) + + ((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(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", fd, + (unsigned long)&packet, len, strerror(errno)); + return -1; + } + + /* Use new queue structure */ + if (queue_newmsg(gsn->queue_req, &qmsg, &addr, gsn->seq_next)) { + gsn->err_queuefull++; + gtp_err(LOG_ERR, __FILE__, __LINE__, + "Retransmit queue is full"); + } else { + memcpy(&qmsg->p, packet, sizeof(union gtp_packet)); + qmsg->l = len; + qmsg->timeout = time(NULL) + 3; /* When to timeout */ + qmsg->retrans = 0; /* No retransmissions so far */ + qmsg->cbp = cbp; + qmsg->type = ntoh8(packet->gtp0.h.type); + qmsg->fd = fd; + } + gsn->seq_next++; /* Count up this time */ + return 0; } /* gtp_conf @@ -475,373 +477,402 @@ int gtp_req(struct gsn_t *gsn, int version, struct pdp_t *pdp, * 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 **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, cbp)) { - gsn->err_seq++; - gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, packet, len, - "Confirmation packet not found in queue"); - return EOF; - } - - return 0; + 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, cbp)) { + gsn->err_seq++; + gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, packet, len, + "Confirmation packet not found in queue"); + return EOF; + } + + return 0; } -int gtp_retrans(struct gsn_t *gsn) { - /* Retransmit any outstanding packets */ - /* Remove from queue if maxretrans exceeded */ - time_t now; - struct qmsg_t *qmsg; - now = time(NULL); - /*printf("Retrans: New beginning %d\n", (int) now);*/ - - while ((!queue_getfirst(gsn->queue_req, &qmsg)) && - (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->cbp); - queue_freemsg(gsn->queue_req, qmsg); - } - else { - 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(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; - qmsg->retrans++; - } - } - - /* Also clean up reply timeouts */ - while ((!queue_getfirst(gsn->queue_resp, &qmsg)) && - (qmsg->timeout < now)) { - /*printf("Retrans (reply) timeout found: %d\n", (int) time(NULL));*/ - queue_freemsg(gsn->queue_resp, qmsg); - } - - return 0; +int gtp_retrans(struct gsn_t *gsn) +{ + /* Retransmit any outstanding packets */ + /* Remove from queue if maxretrans exceeded */ + time_t now; + struct qmsg_t *qmsg; + now = time(NULL); + /*printf("Retrans: New beginning %d\n", (int) now); */ + + while ((!queue_getfirst(gsn->queue_req, &qmsg)) && + (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->cbp); + queue_freemsg(gsn->queue_req, qmsg); + } else { + 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(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; + qmsg->retrans++; + } + } + + /* Also clean up reply timeouts */ + while ((!queue_getfirst(gsn->queue_resp, &qmsg)) && + (qmsg->timeout < now)) { + /*printf("Retrans (reply) timeout found: %d\n", (int) time(NULL)); */ + queue_freemsg(gsn->queue_resp, qmsg); + } + + return 0; } -int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout) { - time_t now, later; - struct qmsg_t *qmsg; - - if (queue_getfirst(gsn->queue_req, &qmsg)) { - timeout->tv_sec = 10; - timeout->tv_usec = 0; - } - else { - now = time(NULL); - later = qmsg->timeout; - timeout->tv_sec = later - now; - timeout->tv_usec = 0; - if (timeout->tv_sec < 0) timeout->tv_sec = 0; /* No negative allowed */ - if (timeout->tv_sec > 10) timeout->tv_sec = 10; /* Max sleep for 10 sec*/ - } - return 0; +int gtp_retranstimeout(struct gsn_t *gsn, struct timeval *timeout) +{ + time_t now, later; + struct qmsg_t *qmsg; + + if (queue_getfirst(gsn->queue_req, &qmsg)) { + timeout->tv_sec = 10; + timeout->tv_usec = 0; + } else { + now = time(NULL); + later = qmsg->timeout; + timeout->tv_sec = later - now; + timeout->tv_usec = 0; + if (timeout->tv_sec < 0) + timeout->tv_sec = 0; /* No negative allowed */ + if (timeout->tv_sec > 10) + timeout->tv_sec = 10; /* Max sleep for 10 sec */ + } + return 0; } -int gtp_resp(int version, struct gsn_t *gsn, struct pdp_t *pdp, +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; - - 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; - } - - if (fcntl(fd, F_SETFL, 0)) { - gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()"); - return -1; - } - - 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", fd, (unsigned long) &packet, len, strerror(errno)); - return -1; - } - - /* Use new queue structure */ - if (queue_newmsg(gsn->queue_resp, &qmsg, peer, seq)) { - gsn->err_queuefull++; - gtp_err(LOG_ERR, __FILE__, __LINE__, "Retransmit queue is full"); - } - else { - memcpy(&qmsg->p, packet, sizeof(union gtp_packet)); - qmsg->l = len; - qmsg->timeout = time(NULL) + 60; /* When to timeout */ - qmsg->retrans = 0; /* No retransmissions so far */ - qmsg->cbp = NULL; - qmsg->type = 0; - qmsg->fd = fd; - } - return 0; + struct sockaddr_in *peer, int fd, uint16_t seq, uint64_t tid) +{ + struct qmsg_t *qmsg; + + 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; + } + + if (fcntl(fd, F_SETFL, 0)) { + gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()"); + return -1; + } + + 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", fd, + (unsigned long)&packet, len, strerror(errno)); + return -1; + } + + /* Use new queue structure */ + if (queue_newmsg(gsn->queue_resp, &qmsg, peer, seq)) { + gsn->err_queuefull++; + gtp_err(LOG_ERR, __FILE__, __LINE__, + "Retransmit queue is full"); + } else { + memcpy(&qmsg->p, packet, sizeof(union gtp_packet)); + qmsg->l = len; + qmsg->timeout = time(NULL) + 60; /* When to timeout */ + qmsg->retrans = 0; /* No retransmissions so far */ + qmsg->cbp = NULL; + qmsg->type = 0; + qmsg->fd = fd; + } + return 0; } int gtp_notification(struct gsn_t *gsn, int version, union gtp_packet *packet, int len, - struct sockaddr_in *peer, int fd, - uint16_t seq) { - - struct sockaddr_in addr; - - memcpy(&addr, peer, sizeof(addr)); - - /* In GTP0 notifications are treated as replies. In GTP1 they - are requests for which there is no reply */ - - if (fd == gsn->fd1c) - addr.sin_port = htons(GTP1C_PORT); - else if (fd == gsn->fd1u) - addr.sin_port = htons(GTP1C_PORT); - - if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */ - packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE); - packet->gtp0.h.seq = hton16(seq); - } - 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); - } - else { - gtp_err(LOG_ERR, __FILE__, __LINE__, "Unknown packet flag"); - return -1; - } - - if (fcntl(fd, F_SETFL, 0)) { - gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()"); - return -1; - } - - if (sendto(fd, packet, len, 0, - (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) < 0) { - gsn->err_sendto++; - 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; - } - return 0; -} + struct sockaddr_in *peer, int fd, uint16_t seq) +{ + + struct sockaddr_in addr; + + memcpy(&addr, peer, sizeof(addr)); + + /* In GTP0 notifications are treated as replies. In GTP1 they + are requests for which there is no reply */ + + if (fd == gsn->fd1c) + addr.sin_port = htons(GTP1C_PORT); + else if (fd == gsn->fd1u) + addr.sin_port = htons(GTP1C_PORT); -int gtp_dublicate(struct gsn_t *gsn, int version, - struct sockaddr_in *peer, uint16_t seq) { - struct qmsg_t *qmsg; - - if(queue_seqget(gsn->queue_resp, &qmsg, peer, seq)) { - return EOF; /* Notfound */ - } - - 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; + if ((packet->flags & 0xe0) == 0x00) { /* Version 0 */ + packet->gtp0.h.length = hton16(len - GTP0_HEADER_SIZE); + packet->gtp0.h.seq = hton16(seq); + } 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); + } else { + gtp_err(LOG_ERR, __FILE__, __LINE__, "Unknown packet flag"); + return -1; + } + + if (fcntl(fd, F_SETFL, 0)) { + gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()"); + return -1; + } + + if (sendto(fd, packet, len, 0, + (struct sockaddr *)&addr, sizeof(struct sockaddr_in)) < 0) { + gsn->err_sendto++; + 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; + } + return 0; } +int gtp_dublicate(struct gsn_t *gsn, int version, + struct sockaddr_in *peer, uint16_t seq) +{ + struct qmsg_t *qmsg; + if (queue_seqget(gsn->queue_resp, &qmsg, peer, seq)) { + return EOF; /* Notfound */ + } + + 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; +} /* Perform restoration and recovery error handling as described in 29.060 */ -static void log_restart(struct gsn_t *gsn) { +static void log_restart(struct gsn_t *gsn) +{ FILE *f; int i, rc; int counter = 0; char filename[NAMESIZE]; - filename[NAMESIZE-1] = 0; /* No null term. guarantee by strncpy */ - strncpy(filename, gsn->statedir, NAMESIZE-1); - strncat(filename, RESTART_FILE, - NAMESIZE-1-sizeof(RESTART_FILE)); + filename[NAMESIZE - 1] = 0; /* No null term. guarantee by strncpy */ + strncpy(filename, gsn->statedir, NAMESIZE - 1); + strncat(filename, RESTART_FILE, NAMESIZE - 1 - sizeof(RESTART_FILE)); i = umask(022); /* We try to open file. On failure we will later try to create file */ if (!(f = fopen(filename, "r"))) { - gtp_err(LOG_ERR, __FILE__, __LINE__, "State information file (%s) not found. Creating new file.", filename); + gtp_err(LOG_ERR, __FILE__, __LINE__, + "State information file (%s) not found. Creating new file.", + filename); + } else { + umask(i); + rc = fscanf(f, "%d", &counter); + if (rc != 1) { + gtp_err(LOG_ERR, __FILE__, __LINE__, + "fscanf failed to read counter value"); + return; + } + if (fclose(f)) { + gtp_err(LOG_ERR, __FILE__, __LINE__, + "fclose failed: Error = %s", strerror(errno)); + } } - else { - umask(i); - rc = fscanf(f, "%d", &counter); - if (rc != 1) { - gtp_err(LOG_ERR, __FILE__, __LINE__, "fscanf failed to read counter value"); - return; - } - if (fclose(f)) { - gtp_err(LOG_ERR, __FILE__, __LINE__, "fclose failed: Error = %s", strerror(errno)); - } - } - - gsn->restart_counter = (unsigned char) counter; + + gsn->restart_counter = (unsigned char)counter; gsn->restart_counter++; - + if (!(f = fopen(filename, "w"))) { - gtp_err(LOG_ERR, __FILE__, __LINE__, "fopen(path=%s, mode=%s) failed: Error = %s", filename, "w", strerror(errno)); - return; + gtp_err(LOG_ERR, __FILE__, __LINE__, + "fopen(path=%s, mode=%s) failed: Error = %s", filename, + "w", strerror(errno)); + return; } umask(i); fprintf(f, "%d\n", gsn->restart_counter); if (fclose(f)) { - gtp_err(LOG_ERR, __FILE__, __LINE__, "fclose failed: Error = %s", strerror(errno)); - return; + gtp_err(LOG_ERR, __FILE__, __LINE__, + "fclose failed: Error = %s", strerror(errno)); + return; } } - - int gtp_new(struct gsn_t **gsn, char *statedir, struct in_addr *listen, - int mode) + int mode) { - struct sockaddr_in addr; - - syslog(LOG_ERR, "GTP: gtp_newgsn() started"); - - *gsn = calloc(sizeof(struct gsn_t), 1); /* TODO */ - - (*gsn)->statedir = statedir; - log_restart(*gsn); - - /* Initialise sequence number */ - (*gsn)->seq_next = (*gsn)->restart_counter * 1024; - - /* Initialise request retransmit queue */ - queue_new(&(*gsn)->queue_req); - queue_new(&(*gsn)->queue_resp); - - /* Initialise pdp table */ - pdp_init(); - - /* Initialise call back functions */ - (*gsn)->cb_create_context_ind = 0; - (*gsn)->cb_delete_context = 0; - (*gsn)->cb_unsup_ind = 0; - (*gsn)->cb_conf = 0; - (*gsn)->cb_data_ind = 0; - - /* Store function parameters */ - (*gsn)->gsnc = *listen; - (*gsn)->gsnu = *listen; - (*gsn)->mode = mode; - - - /* 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; - } - - 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); + struct sockaddr_in addr; + + syslog(LOG_ERR, "GTP: gtp_newgsn() started"); + + *gsn = calloc(sizeof(struct gsn_t), 1); /* TODO */ + + (*gsn)->statedir = statedir; + log_restart(*gsn); + + /* Initialise sequence number */ + (*gsn)->seq_next = (*gsn)->restart_counter * 1024; + + /* Initialise request retransmit queue */ + queue_new(&(*gsn)->queue_req); + queue_new(&(*gsn)->queue_resp); + + /* Initialise pdp table */ + pdp_init(); + + /* Initialise call back functions */ + (*gsn)->cb_create_context_ind = 0; + (*gsn)->cb_delete_context = 0; + (*gsn)->cb_unsup_ind = 0; + (*gsn)->cb_conf = 0; + (*gsn)->cb_data_ind = 0; + + /* Store function parameters */ + (*gsn)->gsnc = *listen; + (*gsn)->gsnu = *listen; + (*gsn)->mode = mode; + + /* 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; + } + + 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 defined(__FreeBSD__) || defined(__APPLE__) - addr.sin_len = sizeof(addr); + addr.sin_len = sizeof(addr); #endif - - 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; - } - - /* 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; - } - - 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)->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; + } + + /* 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; + } + + 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 defined(__FreeBSD__) || defined(__APPLE__) - addr.sin_len = sizeof(addr); + addr.sin_len = sizeof(addr); #endif - - 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 = *listen; /* Same IP for user traffic and signalling*/ - addr.sin_port = htons(GTP1U_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 = *listen; /* Same IP for user traffic and signalling */ + addr.sin_port = htons(GTP1U_PORT); #if defined(__FreeBSD__) || defined(__APPLE__) - addr.sin_len = sizeof(addr); + addr.sin_len = sizeof(addr); #endif - - if (bind((*gsn)->fd1u, (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; - } - - return 0; + + if (bind((*gsn)->fd1u, (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; + } + + return 0; } -int gtp_free(struct gsn_t *gsn) { +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); + /* Clean up retransmit queues */ + queue_free(gsn->queue_req); + queue_free(gsn->queue_resp); - free(gsn); - return 0; + close(gsn->fd0); + close(gsn->fd1c); + close(gsn->fd1u); + + free(gsn); + return 0; } /* *********************************************************** @@ -877,71 +908,78 @@ int gtp_free(struct gsn_t *gsn) { int gtp_echo_req(struct gsn_t *gsn, int version, void *cbp, struct in_addr *inetaddr) { - union gtp_packet packet; - unsigned int length = get_default_gtp(version, GTP_ECHO_REQ, &packet); - return gtp_req(gsn, version, NULL, &packet, length, inetaddr, cbp); + union gtp_packet packet; + unsigned int length = get_default_gtp(version, GTP_ECHO_REQ, &packet); + return gtp_req(gsn, version, NULL, &packet, length, inetaddr, cbp); } /* 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) + struct sockaddr_in *peer, int fd, void *pack, unsigned len) { - union gtp_packet packet; - unsigned 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)); + union gtp_packet packet; + unsigned 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, int version, struct sockaddr_in *peer, - int fd, void *pack, unsigned len) { +int gtp_echo_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer, + int fd, void *pack, unsigned len) +{ - /* Check if it was a dublicate request */ - if(!gtp_dublicate(gsn, 0, peer, get_seq(pack))) return 0; + /* Check if it was a dublicate request */ + if (!gtp_dublicate(gsn, 0, peer, get_seq(pack))) + return 0; - /* Send off reply to request */ - return gtp_echo_resp(gsn, version, peer, fd, 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, int version, struct sockaddr_in *peer, - void *pack, unsigned len) { - union gtpie_member *ie[GTPIE_SIZE]; - unsigned char recovery; - void *cbp = NULL; - uint8_t type = 0; - int hlen = get_hlen(pack); - - /* Remove packet from queue */ - if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp)) return EOF; - - /* 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; - } - - if (gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { - 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; - } - - /* 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); - - if (gsn->cb_recovery) gsn->cb_recovery(peer, recovery); - - return 0; + void *pack, unsigned len) +{ + union gtpie_member *ie[GTPIE_SIZE]; + unsigned char recovery; + void *cbp = NULL; + uint8_t type = 0; + int hlen = get_hlen(pack); + + /* Remove packet from queue */ + if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp)) + return EOF; + + /* 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; + } + + if (gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { + 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; + } + + /* 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); + + if (gsn->cb_recovery) + gsn->cb_recovery(peer, recovery); + + return 0; } /* Send off a Version Not Supported message */ @@ -958,52 +996,55 @@ int gtp_echo_conf(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int gtp_unsup_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len) { - union gtp_packet packet; + union gtp_packet packet; - /* GTP 1 is the highest supported protocol */ - unsigned int length = get_default_gtp(1, GTP_NOT_SUPPORTED, &packet); - return gtp_notification(gsn, version, &packet, length, - peer, fd, 0); + /* GTP 1 is the highest supported protocol */ + unsigned int length = get_default_gtp(1, GTP_NOT_SUPPORTED, &packet); + return gtp_notification(gsn, version, &packet, length, peer, fd, 0); } /* Handle a Version Not Supported message */ -int gtp_unsup_ind(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) +{ - if (gsn->cb_unsup_ind) gsn->cb_unsup_ind(peer); - - return 0; + if (gsn->cb_unsup_ind) + gsn->cb_unsup_ind(peer); + + return 0; } /* Send off an Supported Extension Headers Notification */ int gtp_extheader_req(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len) { - union gtp_packet packet; - unsigned int length = get_default_gtp(version, GTP_SUPP_EXT_HEADER, &packet); + union gtp_packet packet; + unsigned int length = + get_default_gtp(version, GTP_SUPP_EXT_HEADER, &packet); - uint8_t pdcp_pdu = GTP_EXT_PDCP_PDU; + uint8_t pdcp_pdu = GTP_EXT_PDCP_PDU; - if (version < 1) - return 0; + if (version < 1) + return 0; - /* We report back that we support only PDCP PDU headers */ - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EXT_HEADER_T, sizeof(pdcp_pdu), - &pdcp_pdu); + /* We report back that we support only PDCP PDU headers */ + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EXT_HEADER_T, + sizeof(pdcp_pdu), &pdcp_pdu); - return gtp_notification(gsn, version, &packet, length, - peer, fd, get_seq(pack)); + return gtp_notification(gsn, version, &packet, length, + peer, fd, get_seq(pack)); } /* Handle a Supported Extension Headers Notification */ -int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer, - void *pack, unsigned len) { +int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer, + void *pack, unsigned len) +{ - if (gsn->cb_extheader_ind) gsn->cb_extheader_ind(peer); - - return 0; -} + if (gsn->cb_extheader_ind) + gsn->cb_extheader_ind(peer); + return 0; +} /* *********************************************************** * Session management messages @@ -1018,1466 +1059,1564 @@ int gtp_extheader_ind(struct gsn_t *gsn, struct sockaddr_in *peer, *************************************************************/ /* API: Send Create PDP Context Request (7.3.1) */ -extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp, - void *cbp) { - union gtp_packet packet; - unsigned int length = get_default_gtp(pdp->version, GTP_CREATE_PDP_REQ, &packet); - struct pdp_t *linked_pdp = NULL; - - /* TODO: Secondary PDP Context Activation Procedure */ - /* In secondary activation procedure the PDP context is identified - by tei in the header. The following fields are omitted: Selection - mode, IMSI, MSISDN, End User Address, Access Point Name and - Protocol Configuration Options */ - - if (pdp->secondary) { - if (pdp_getgtp1(&linked_pdp, pdp->teic_own)) { - gtp_err(LOG_ERR, __FILE__, __LINE__, "Unknown linked PDP context"); - return EOF; - } - } - - if (pdp->version == 0) { - gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE0, - sizeof(pdp->qos_req0), pdp->qos_req0); - } - - /* Section 7.7.2 */ - if (pdp->version == 1) { - if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ - gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_IMSI, - sizeof(pdp->imsi), (uint8_t*) &pdp->imsi); - } - - /* Section 7.7.3 Routing Area Information */ - if (pdp->rai_given == 1) - gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_RAI, - pdp->rai.l, (uint8_t*) &pdp->rai.v); - - - /* Section 7.7.11 */ - if (pdp->norecovery_given == 0) - gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY, - gsn->restart_counter); - - /* Section 7.7.12 */ - if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ - 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); - } - - /* Section 7.7.13 */ - if (pdp->version == 1) { - gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_DI, - pdp->teid_own); - - /* Section 7.7.14 */ - if (!pdp->teic_confirmed) - gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_C, - pdp->teic_own); - - /* Section 7.7.17 */ - gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI, - pdp->nsapi); - - /* Section 7.7.17 */ - if (pdp->secondary) /* Secondary PDP Context Activation Procedure */ - gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI, - linked_pdp->nsapi); - - /* Section 7.7.23 */ - if (pdp->cch_pdp) /* Only include charging if flags are set */ - gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_CHARGING_C, - pdp->cch_pdp); - } - - /* TODO - gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_REF, - pdp->traceref); - gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_TYPE, - pdp->tracetype); */ - - /* Section 7.7.27 */ - if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA, - pdp->eua.l, pdp->eua.v); - - - /* Section 7.7.30 */ - if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_APN, - pdp->apn_use.l, pdp->apn_use.v); - - /* Section 7.7.31 */ - if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ - if (pdp->pco_req.l) - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_PCO, - pdp->pco_req.l, pdp->pco_req.v); - - /* Section 7.7.32 */ - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR, - pdp->gsnlc.l, pdp->gsnlc.v); - /* Section 7.7.32 */ - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR, - pdp->gsnlu.l, pdp->gsnlu.v); - - /* Section 7.7.33 */ - if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_MSISDN, - pdp->msisdn.l, pdp->msisdn.v); - - /* Section 7.7.34 */ - if (pdp->version == 1) - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE, - pdp->qos_req.l, pdp->qos_req.v); - - /* Section 7.7.36 */ - if ((pdp->version == 1) && pdp->tft.l) - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_TFT, - pdp->tft.l, pdp->tft.v); - - /* Section 7.7.41 */ - if ((pdp->version == 1) && pdp->triggerid.l) - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_TRIGGER_ID, - pdp->triggerid.l, pdp->triggerid.v); - - /* Section 7.7.42 */ - if ((pdp->version == 1) && pdp->omcid.l) - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_OMC_ID, - pdp->omcid.l, pdp->omcid.v); - - /* new R7 fields */ - if (pdp->rattype_given == 1) - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_RAT_TYPE, - pdp->rattype.l, pdp->rattype.v); - - if (pdp->userloc_given == 1) - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_USER_LOC, - pdp->userloc.l, pdp->userloc.v); - - if (pdp->mstz_given == 1) - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_MS_TZ, - pdp->mstz.l, pdp->mstz.v); - - if (pdp->imeisv_given == 1) - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_IMEI_SV, - pdp->imeisv.l, pdp->imeisv.v); - - /* TODO hisaddr0 */ - gtp_req(gsn, pdp->version, pdp, &packet, length, &pdp->hisaddr0, cbp); - - return 0; +extern int gtp_create_context_req(struct gsn_t *gsn, struct pdp_t *pdp, + void *cbp) +{ + union gtp_packet packet; + unsigned int length = + get_default_gtp(pdp->version, GTP_CREATE_PDP_REQ, &packet); + struct pdp_t *linked_pdp = NULL; + + /* TODO: Secondary PDP Context Activation Procedure */ + /* In secondary activation procedure the PDP context is identified + by tei in the header. The following fields are omitted: Selection + mode, IMSI, MSISDN, End User Address, Access Point Name and + Protocol Configuration Options */ + + if (pdp->secondary) { + if (pdp_getgtp1(&linked_pdp, pdp->teic_own)) { + gtp_err(LOG_ERR, __FILE__, __LINE__, + "Unknown linked PDP context"); + return EOF; + } + } + + if (pdp->version == 0) { + gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE0, + sizeof(pdp->qos_req0), pdp->qos_req0); + } + + /* Section 7.7.2 */ + if (pdp->version == 1) { + if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ + gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_IMSI, + sizeof(pdp->imsi), (uint8_t *) & pdp->imsi); + } + + /* Section 7.7.3 Routing Area Information */ + if (pdp->rai_given == 1) + gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_RAI, + pdp->rai.l, (uint8_t *) & pdp->rai.v); + + /* Section 7.7.11 */ + if (pdp->norecovery_given == 0) + gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY, + gsn->restart_counter); + + /* Section 7.7.12 */ + if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ + 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); + } + + /* Section 7.7.13 */ + if (pdp->version == 1) { + gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_DI, + pdp->teid_own); + + /* Section 7.7.14 */ + if (!pdp->teic_confirmed) + gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_C, + pdp->teic_own); + + /* Section 7.7.17 */ + gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI, pdp->nsapi); + + /* Section 7.7.17 */ + if (pdp->secondary) /* Secondary PDP Context Activation Procedure */ + gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI, + linked_pdp->nsapi); + + /* Section 7.7.23 */ + if (pdp->cch_pdp) /* Only include charging if flags are set */ + gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_CHARGING_C, + pdp->cch_pdp); + } + + /* TODO + gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_REF, + pdp->traceref); + gtpie_tv2(&packet, &length, GTP_MAX, GTPIE_TRACE_TYPE, + pdp->tracetype); */ + + /* Section 7.7.27 */ + if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA, + pdp->eua.l, pdp->eua.v); + + /* Section 7.7.30 */ + if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_APN, + pdp->apn_use.l, pdp->apn_use.v); + + /* Section 7.7.31 */ + if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ + if (pdp->pco_req.l) + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_PCO, + pdp->pco_req.l, pdp->pco_req.v); + + /* Section 7.7.32 */ + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR, + pdp->gsnlc.l, pdp->gsnlc.v); + /* Section 7.7.32 */ + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_GSN_ADDR, + pdp->gsnlu.l, pdp->gsnlu.v); + + /* Section 7.7.33 */ + if (!pdp->secondary) /* Not Secondary PDP Context Activation Procedure */ + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_MSISDN, + pdp->msisdn.l, pdp->msisdn.v); + + /* Section 7.7.34 */ + if (pdp->version == 1) + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE, + pdp->qos_req.l, pdp->qos_req.v); + + /* Section 7.7.36 */ + if ((pdp->version == 1) && pdp->tft.l) + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_TFT, + pdp->tft.l, pdp->tft.v); + + /* Section 7.7.41 */ + if ((pdp->version == 1) && pdp->triggerid.l) + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_TRIGGER_ID, + pdp->triggerid.l, pdp->triggerid.v); + + /* Section 7.7.42 */ + if ((pdp->version == 1) && pdp->omcid.l) + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_OMC_ID, + pdp->omcid.l, pdp->omcid.v); + + /* new R7 fields */ + if (pdp->rattype_given == 1) + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_RAT_TYPE, + pdp->rattype.l, pdp->rattype.v); + + if (pdp->userloc_given == 1) + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_USER_LOC, + pdp->userloc.l, pdp->userloc.v); + + if (pdp->mstz_given == 1) + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_MS_TZ, + pdp->mstz.l, pdp->mstz.v); + + if (pdp->imeisv_given == 1) + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_IMEI_SV, + pdp->imeisv.l, pdp->imeisv.v); + + /* TODO hisaddr0 */ + gtp_req(gsn, pdp->version, pdp, &packet, length, &pdp->hisaddr0, cbp); + + return 0; } /* 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; +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; } /* 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)) + int (*cb_create_context_ind) (struct pdp_t * + pdp)) { - gsn->cb_create_context_ind = cb_create_context_ind; - return 0; + gsn->cb_create_context_ind = cb_create_context_ind; + return 0; } - /* 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; - unsigned 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); - - gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_REORDER, - pdp->reorder); - gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY, - gsn->restart_counter); - - 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); - } - - /* TODO: We use teic_own as charging ID */ - gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_CHARGING_ID, - pdp->teic_own); - - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA, - pdp->eua.l, pdp->eua.v); - - if (pdp->pco_neg.l) { /* Optional PCO */ - gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_PCO, - pdp->pco_neg.l, pdp->pco_neg.v); - } - - 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); - - 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(version, gsn, pdp, &packet, length, &pdp->sa_peer, - pdp->fd, pdp->seq, pdp->tid); +int gtp_create_pdp_resp(struct gsn_t *gsn, int version, struct pdp_t *pdp, + uint8_t cause) +{ + union gtp_packet packet; + unsigned 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); + + gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_REORDER, + pdp->reorder); + gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY, + gsn->restart_counter); + + 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); + } + + /* TODO: We use teic_own as charging ID */ + gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_CHARGING_ID, + pdp->teic_own); + + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_EUA, + pdp->eua.l, pdp->eua.v); + + if (pdp->pco_neg.l) { /* Optional PCO */ + gtpie_tlv(&packet, &length, GTP_MAX, GTPIE_PCO, + pdp->pco_neg.l, pdp->pco_neg.v); + } + + 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); + + 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(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, 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; - - uint16_t seq = get_seq(pack); - int hlen = get_hlen(pack); - uint8_t linked_nsapi = 0; - struct pdp_t *linked_pdp = NULL; - - if(!gtp_dublicate(gsn, version, 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 & 0x0fffffffffffffffull; - pdp->nsapi = (((union gtp_packet*)pack)->gtp0.h.tid & 0xf000000000000000ull) >> 60; - } - - pdp->seq = seq; - pdp->sa_peer = *peer; - pdp->fd = fd; - pdp->version = version; - - /* Decode information elements */ - 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, pdp, GTPCAUSE_INVALID_MESSAGE); - } - - if (version == 1) { - /* Linked NSAPI (conditional) */ - /* If included this is the Secondary PDP Context Activation Procedure */ - /* In secondary activation IMSI is not included, so the context must be */ - /* identified by the tei */ - if (!gtpie_gettv1(ie, GTPIE_NSAPI, 1, &linked_nsapi)) { - - /* Find the primary PDP context */ - if (pdp_getgtp1(&linked_pdp, get_tei(pack))) { - gsn->incorrect++; - gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len, - "Incorrect optional information field"); - return gtp_create_pdp_resp(gsn, version, pdp, - GTPCAUSE_OPT_IE_INCORRECT); - } - - /* Check that the primary PDP context matches linked nsapi */ - if (linked_pdp->nsapi != linked_nsapi) { - gsn->incorrect++; - gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len, - "Incorrect optional information field"); - return gtp_create_pdp_resp(gsn, version, pdp, - GTPCAUSE_OPT_IE_INCORRECT); - } - - /* Copy parameters from primary context */ - pdp->selmode = linked_pdp->selmode; - pdp->imsi = linked_pdp->imsi; - pdp->msisdn = linked_pdp->msisdn; - pdp->eua = linked_pdp->eua; - pdp->pco_req = linked_pdp->pco_req; - pdp->apn_req = linked_pdp->apn_req; - pdp->teic_gn = linked_pdp->teic_gn; - pdp->secondary = 1; - } - } /* if (version == 1) */ - - 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); - } - } - - if ((version == 1) && (!linked_pdp)) { - /* Not Secondary PDP Context Activation Procedure */ - /* 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); - } - } - - /* Recovery (optional) */ - if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { - if (gsn->cb_recovery) gsn->cb_recovery(peer, recovery); - } - - /* Selection mode (conditional) */ - if (!linked_pdp) { /* Not Secondary PDP Context Activation Procedure */ - 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, 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 (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 (!linked_pdp) { /* Not Secondary PDP Context Activation Procedure */ - 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); - } - } - - - /* Charging Characteriatics (optional) */ - /* Trace reference (optional) */ - /* Trace type (optional) */ - /* Charging Characteriatics (optional) */ - - if (!linked_pdp) { /* Not Secondary PDP Context Activation Procedure */ - /* 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, 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, 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))) { - } - } - - /* 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, 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, pdp, - GTPCAUSE_MAN_IE_MISSING); - } - - if (!linked_pdp) { /* Not Secondary PDP Context Activation Procedure */ - /* 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, 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_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) - && (!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) - && (!memcmp(pdp->msisdn.v, pdp_old->msisdn.v, pdp->msisdn.l))) { - /* OK! We are dealing with the same APN. We will copy new - * parameters to the old pdp and send off confirmation - * We ignore the following information elements: - * QoS: MS will get originally negotiated QoS. - * End user address (EUA). MS will get old EUA anyway. - * Protocol configuration option (PCO): Only application can verify */ - - if (GTP_DEBUG) printf("gtp_create_pdp_ind: Old context found\n"); - - /* Copy remote flow label */ - 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); - - /* Copy request parameters */ - pdp_old->seq = pdp->seq; - pdp_old->sa_peer = pdp->sa_peer; - pdp_old->fd = pdp->fd = fd; - pdp_old->version = pdp->version = version; - - /* Switch to using the old pdp context */ - pdp = pdp_old; - - /* Confirm to peer that things were "successful" */ - return gtp_create_pdp_resp(gsn, version, pdp, GTPCAUSE_ACC_REQ); - } - else { /* This is not the same PDP context. Delete the old one. */ - - if (GTP_DEBUG) printf("gtp_create_pdp_ind: Deleting old context\n"); - - 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, pdp->imsi, pdp->nsapi, pdp); - - /* Callback function to validata login */ - if (gsn->cb_create_context_ind !=0) - return gsn->cb_create_context_ind(pdp); - else { - 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); - } -} + 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; + + uint16_t seq = get_seq(pack); + int hlen = get_hlen(pack); + uint8_t linked_nsapi = 0; + struct pdp_t *linked_pdp = NULL; + + if (!gtp_dublicate(gsn, version, 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 & 0x0fffffffffffffffull; + pdp->nsapi = + (((union gtp_packet *)pack)->gtp0. + h.tid & 0xf000000000000000ull) >> 60; + } + + pdp->seq = seq; + pdp->sa_peer = *peer; + pdp->fd = fd; + pdp->version = version; + + /* Decode information elements */ + 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, pdp, + GTPCAUSE_INVALID_MESSAGE); + } + + if (version == 1) { + /* Linked NSAPI (conditional) */ + /* If included this is the Secondary PDP Context Activation Procedure */ + /* In secondary activation IMSI is not included, so the context must be */ + /* identified by the tei */ + if (!gtpie_gettv1(ie, GTPIE_NSAPI, 1, &linked_nsapi)) { + + /* Find the primary PDP context */ + if (pdp_getgtp1(&linked_pdp, get_tei(pack))) { + gsn->incorrect++; + gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, + pack, len, + "Incorrect optional information field"); + return gtp_create_pdp_resp(gsn, version, pdp, + GTPCAUSE_OPT_IE_INCORRECT); + } + + /* Check that the primary PDP context matches linked nsapi */ + if (linked_pdp->nsapi != linked_nsapi) { + gsn->incorrect++; + gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, + pack, len, + "Incorrect optional information field"); + return gtp_create_pdp_resp(gsn, version, pdp, + GTPCAUSE_OPT_IE_INCORRECT); + } + + /* Copy parameters from primary context */ + pdp->selmode = linked_pdp->selmode; + pdp->imsi = linked_pdp->imsi; + pdp->msisdn = linked_pdp->msisdn; + pdp->eua = linked_pdp->eua; + pdp->pco_req = linked_pdp->pco_req; + pdp->apn_req = linked_pdp->apn_req; + pdp->teic_gn = linked_pdp->teic_gn; + pdp->secondary = 1; + } + } + /* if (version == 1) */ + 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); + } + } + + if ((version == 1) && (!linked_pdp)) { + /* Not Secondary PDP Context Activation Procedure */ + /* 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); + } + } + + /* Recovery (optional) */ + if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { + if (gsn->cb_recovery) + gsn->cb_recovery(peer, recovery); + } + + /* Selection mode (conditional) */ + if (!linked_pdp) { /* Not Secondary PDP Context Activation Procedure */ + 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, 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 (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 (!linked_pdp) { /* Not Secondary PDP Context Activation Procedure */ + 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); + } + } + + /* Charging Characteriatics (optional) */ + /* Trace reference (optional) */ + /* Trace type (optional) */ + /* Charging Characteriatics (optional) */ + + if (!linked_pdp) { /* Not Secondary PDP Context Activation Procedure */ + /* 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, 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, 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))) { + } + } + /* 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, 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, pdp, + GTPCAUSE_MAN_IE_MISSING); + } + + if (!linked_pdp) { /* Not Secondary PDP Context Activation Procedure */ + /* 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, 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_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) + && + (!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) + && + (!memcmp(pdp->msisdn.v, pdp_old->msisdn.v, pdp->msisdn.l))) + { + /* OK! We are dealing with the same APN. We will copy new + * parameters to the old pdp and send off confirmation + * We ignore the following information elements: + * QoS: MS will get originally negotiated QoS. + * End user address (EUA). MS will get old EUA anyway. + * Protocol configuration option (PCO): Only application can verify */ + + if (GTP_DEBUG) + printf + ("gtp_create_pdp_ind: Old context found\n"); + + /* Copy remote flow label */ + 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); + + /* Copy request parameters */ + pdp_old->seq = pdp->seq; + pdp_old->sa_peer = pdp->sa_peer; + pdp_old->fd = pdp->fd = fd; + pdp_old->version = pdp->version = version; + + /* Switch to using the old pdp context */ + pdp = pdp_old; + + /* Confirm to peer that things were "successful" */ + return gtp_create_pdp_resp(gsn, version, pdp, + GTPCAUSE_ACC_REQ); + } else { /* This is not the same PDP context. Delete the old one. */ + + if (GTP_DEBUG) + printf + ("gtp_create_pdp_ind: Deleting old context\n"); + + 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, pdp->imsi, pdp->nsapi, pdp); + + /* Callback function to validata login */ + if (gsn->cb_create_context_ind != 0) + return gsn->cb_create_context_ind(pdp); + else { + 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 pdp_t *pdp; - union gtpie_member *ie[GTPIE_SIZE]; - uint8_t cause, recovery; - void *cbp = NULL; - uint8_t type = 0; - int hlen = get_hlen(pack); - - /* Remove packet from queue */ - if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp)) return EOF; - - /* 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 (gsn->cb_conf) gsn->cb_conf(type, EOF, NULL, cbp); - return EOF; - } - - /* Register that we have received a valid teic from GGSN */ - pdp->teic_confirmed = 1; - - /* Decode information elements */ - 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); - /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - pdp_freepdp(pdp); */ - return EOF; - } - - /* 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); - /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - pdp_freepdp(pdp); */ - return EOF; - } - - /* Extract recovery (optional) */ - if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { - if (gsn->cb_recovery) gsn->cb_recovery(peer, recovery); - } - - /* 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))) { - } - - /* Check all conditional information elements */ - if (GTPCAUSE_ACC_REQ == cause) { - - 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); - /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - pdp_freepdp(pdp); */ - return EOF; - } - } - - 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, cbp); - /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - pdp_freepdp(pdp); */ - 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); - /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - pdp_freepdp(pdp); */ - 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); - /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - pdp_freepdp(pdp); */ - 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); - /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - pdp_freepdp(pdp); */ - 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); - /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - pdp_freepdp(pdp); */ - 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, cbp); - /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - pdp_freepdp(pdp); */ - } - - 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 conditional information field"); - 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; - } - - 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 conditional information field"); - 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; - } - - 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 conditional information field"); - 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; - } - - 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); - /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - pdp_freepdp(pdp); */ - return EOF; - } - } - - } - - if (gsn->cb_conf) gsn->cb_conf(type, cause, pdp, cbp); - - return 0; -} + struct sockaddr_in *peer, void *pack, unsigned len) +{ + struct pdp_t *pdp; + union gtpie_member *ie[GTPIE_SIZE]; + uint8_t cause, recovery; + void *cbp = NULL; + uint8_t type = 0; + int hlen = get_hlen(pack); + + /* Remove packet from queue */ + if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp)) + return EOF; + + /* 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 (gsn->cb_conf) + gsn->cb_conf(type, EOF, NULL, cbp); + return EOF; + } + /* Register that we have received a valid teic from GGSN */ + pdp->teic_confirmed = 1; + + /* Decode information elements */ + 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); + /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); + pdp_freepdp(pdp); */ + return EOF; + } + + /* 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); + /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); + pdp_freepdp(pdp); */ + return EOF; + } + + /* Extract recovery (optional) */ + if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { + if (gsn->cb_recovery) + gsn->cb_recovery(peer, recovery); + } + + /* 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))) { + } + + /* Check all conditional information elements */ + if (GTPCAUSE_ACC_REQ == cause) { + + 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); + /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); + pdp_freepdp(pdp); */ + return EOF; + } + } + + 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, cbp); + /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); + pdp_freepdp(pdp); */ + 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); + /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); + pdp_freepdp(pdp); */ + 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); + /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); + pdp_freepdp(pdp); */ + 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); + /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); + pdp_freepdp(pdp); */ + 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); + /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); + pdp_freepdp(pdp); */ + 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, cbp); + /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); + pdp_freepdp(pdp); */ + } + + 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 conditional information field"); + 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; + } + + 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 conditional information field"); + 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; + } + + 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 conditional information field"); + 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; + } + + 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); + /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); + pdp_freepdp(pdp); */ + return EOF; + } + } + + } + + if (gsn->cb_conf) + gsn->cb_conf(type, cause, pdp, cbp); + + return 0; +} /* 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; - unsigned int length = get_default_gtp(pdp->version, GTP_UPDATE_PDP_REQ, &packet); - - if (pdp->version == 0) - gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE0, - sizeof(pdp->qos_req0), pdp->qos_req0); - - /* Include IMSI if updating with unknown teic_gn */ - if ((pdp->version == 1) && (!pdp->teic_gn)) - 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); - - 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); - - if (!pdp->teic_confirmed) - 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, &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); - - 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 0; -} + struct in_addr *inetaddr) +{ + union gtp_packet packet; + unsigned int length = + get_default_gtp(pdp->version, GTP_UPDATE_PDP_REQ, &packet); + + if (pdp->version == 0) + gtpie_tv0(&packet, &length, GTP_MAX, GTPIE_QOS_PROFILE0, + sizeof(pdp->qos_req0), pdp->qos_req0); + + /* Include IMSI if updating with unknown teic_gn */ + if ((pdp->version == 1) && (!pdp->teic_gn)) + 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); + + 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); + + if (!pdp->teic_confirmed) + 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, &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); + + 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 0; +} /* Send Update PDP Context Response */ -int gtp_update_pdp_resp(struct gsn_t *gsn, int version, - struct sockaddr_in *peer, int fd, +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; - unsigned int length = get_default_gtp(version, GTP_UPDATE_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); - - gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY, - gsn->restart_counter); - - 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); - - if (!pdp->teic_confirmed) - gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_TEI_C, - pdp->teic_own); - } - - /* TODO we use teid_own as charging ID address */ - gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_CHARGING_ID, - pdp->teid_own); - - /* 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, &length, GTP_MAX, GTPIE_GSN_ADDR, - pdp->gsnlu.l, pdp->gsnlu.v); - - 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(version, gsn, pdp, &packet, length, peer, - fd, get_seq(pack), get_tid(pack)); -} + struct pdp_t *pdp, uint8_t cause) +{ + + union gtp_packet packet; + unsigned int length = + get_default_gtp(version, GTP_UPDATE_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); + + gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_RECOVERY, + gsn->restart_counter); + + 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); + + if (!pdp->teic_confirmed) + gtpie_tv4(&packet, &length, GTP_MAX, + GTPIE_TEI_C, pdp->teic_own); + } + + /* TODO we use teid_own as charging ID address */ + gtpie_tv4(&packet, &length, GTP_MAX, GTPIE_CHARGING_ID, + pdp->teid_own); + + /* 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, &length, GTP_MAX, GTPIE_GSN_ADDR, + pdp->gsnlu.l, pdp->gsnlu.v); + + 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(version, gsn, pdp, &packet, length, peer, + fd, get_seq(pack), get_tid(pack)); +} /* Handle Update PDP Context Request */ int gtp_update_pdp_ind(struct gsn_t *gsn, int version, - 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 = get_seq(pack); - int hlen = get_hlen(pack); - - uint64_t imsi; - uint8_t nsapi; - - /* Is this a dublicate ? */ - if(!gtp_dublicate(gsn, version, peer, seq)) { - return 0; /* We allready send of response once */ - } - - - /* Decode information elements */ - 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, fd, pack, len, - NULL, GTPCAUSE_INVALID_MESSAGE); - } - - /* 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 & 0x0fffffffffffffffull; - nsapi = (((union gtp_packet*)pack)->gtp0.h.tid & 0xf000000000000000ull) >> 60; - - /* 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 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); - } - - /* 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)) { - if (gsn->cb_recovery) gsn->cb_recovery(peer, recovery); - } - - 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 (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 */ - /* TODO: From 29.060 it is not clear if TEI_C MUST be included for */ - /* all updated contexts, or only for one of the linked contexts */ - 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"); - memcpy(pdp, &pdp_backup, sizeof(pdp_backup)); - return gtp_update_pdp_resp(gsn, version, pdp, - GTPCAUSE_MAN_IE_MISSING); - } */ - - - /* 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"); - memcpy(pdp, &pdp_backup, sizeof(pdp_backup)); - return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, 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"); - 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); - } - - /* 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, fd, pack, len, pdp, - GTPCAUSE_ACC_REQ); -} + 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 = get_seq(pack); + int hlen = get_hlen(pack); + + uint64_t imsi; + uint8_t nsapi; + + /* Is this a dublicate ? */ + if (!gtp_dublicate(gsn, version, peer, seq)) { + return 0; /* We allready send of response once */ + } + + /* Decode information elements */ + 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, fd, pack, + len, NULL, + GTPCAUSE_INVALID_MESSAGE); + } + + /* 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 & 0x0fffffffffffffffull; + nsapi = + (((union gtp_packet *)pack)->gtp0. + h.tid & 0xf000000000000000ull) >> 60; + + /* 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 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); + } + + /* 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)) { + if (gsn->cb_recovery) + gsn->cb_recovery(peer, recovery); + } + + 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 (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 */ + /* TODO: From 29.060 it is not clear if TEI_C MUST be included for */ + /* all updated contexts, or only for one of the linked contexts */ + 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"); + memcpy(pdp, &pdp_backup, sizeof(pdp_backup)); + return gtp_update_pdp_resp(gsn, version, pdp, + GTPCAUSE_MAN_IE_MISSING); + } */ + + /* 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"); + memcpy(pdp, &pdp_backup, sizeof(pdp_backup)); + return gtp_update_pdp_resp(gsn, version, peer, fd, pack, len, + 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"); + 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); + } + + /* 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, fd, pack, len, pdp, + GTPCAUSE_ACC_REQ); +} /* Handle Update PDP Context Response */ int gtp_update_pdp_conf(struct gsn_t *gsn, int version, - struct sockaddr_in *peer, - void *pack, unsigned len) { - struct pdp_t *pdp; - union gtpie_member *ie[GTPIE_SIZE]; - uint8_t cause, recovery; - void *cbp = NULL; - uint8_t type = 0; - - /* Remove packet from queue */ - 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, cbp); - return EOF; - } - - /* Register that we have received a valid teic from GGSN */ - pdp->teic_confirmed = 1; - - /* Decode information elements */ - 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, cbp); - /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - pdp_freepdp(pdp); */ - return EOF; - } - - /* 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); - /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - pdp_freepdp(pdp); */ - return EOF; - } - - /* Extract recovery (optional) */ - if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { - if (gsn->cb_recovery) gsn->cb_recovery(peer, recovery); - } - - /* Check all conditional information elements */ - if (GTPCAUSE_ACC_REQ != cause) { - 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; - } - else { - /* Check for missing conditionary information elements */ - if (!(gtpie_exist(ie, GTPIE_QOS_PROFILE0, 0) && - gtpie_exist(ie, GTPIE_REORDER, 0) && - gtpie_exist(ie, GTPIE_FL_DI, 0) && - gtpie_exist(ie, GTPIE_FL_C, 0) && - gtpie_exist(ie, GTPIE_CHARGING_ID, 0) && - gtpie_exist(ie, GTPIE_EUA, 0) && - gtpie_exist(ie, GTPIE_GSN_ADDR, 0) && - gtpie_exist(ie, GTPIE_GSN_ADDR, 1))) { - 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); - /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - pdp_freepdp(pdp); */ - return EOF; - } - - /* Update pdp with new values */ - gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0, - pdp->qos_neg0, sizeof(pdp->qos_neg0)); - gtpie_gettv1(ie, GTPIE_REORDER, 0, &pdp->reorder); - gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp->flru); - gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp->flrc); - gtpie_gettv4(ie, GTPIE_CHARGING_ID, 0, &pdp->cid); - gtpie_gettlv(ie, GTPIE_EUA, 0, &pdp->eua.l, - &pdp->eua.v, sizeof(pdp->eua.v)); - gtpie_gettlv(ie, GTPIE_GSN_ADDR, 0, &pdp->gsnrc.l, - &pdp->gsnrc.v, sizeof(pdp->gsnrc.v)); - 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, cbp); - return 0; /* Succes */ - } -} + struct sockaddr_in *peer, void *pack, unsigned len) +{ + struct pdp_t *pdp; + union gtpie_member *ie[GTPIE_SIZE]; + uint8_t cause, recovery; + void *cbp = NULL; + uint8_t type = 0; + + /* Remove packet from queue */ + 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, cbp); + return EOF; + } + /* Register that we have received a valid teic from GGSN */ + pdp->teic_confirmed = 1; + + /* Decode information elements */ + 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, cbp); + /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); + pdp_freepdp(pdp); */ + return EOF; + } + + /* 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); + /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); + pdp_freepdp(pdp); */ + return EOF; + } + + /* Extract recovery (optional) */ + if (!gtpie_gettv1(ie, GTPIE_RECOVERY, 0, &recovery)) { + if (gsn->cb_recovery) + gsn->cb_recovery(peer, recovery); + } + + /* Check all conditional information elements */ + if (GTPCAUSE_ACC_REQ != cause) { + 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; + } else { + /* Check for missing conditionary information elements */ + if (!(gtpie_exist(ie, GTPIE_QOS_PROFILE0, 0) && + gtpie_exist(ie, GTPIE_REORDER, 0) && + gtpie_exist(ie, GTPIE_FL_DI, 0) && + gtpie_exist(ie, GTPIE_FL_C, 0) && + gtpie_exist(ie, GTPIE_CHARGING_ID, 0) && + gtpie_exist(ie, GTPIE_EUA, 0) && + gtpie_exist(ie, GTPIE_GSN_ADDR, 0) && + gtpie_exist(ie, GTPIE_GSN_ADDR, 1))) { + 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); + /* if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); + pdp_freepdp(pdp); */ + return EOF; + } + + /* Update pdp with new values */ + gtpie_gettv0(ie, GTPIE_QOS_PROFILE0, 0, + pdp->qos_neg0, sizeof(pdp->qos_neg0)); + gtpie_gettv1(ie, GTPIE_REORDER, 0, &pdp->reorder); + gtpie_gettv2(ie, GTPIE_FL_DI, 0, &pdp->flru); + gtpie_gettv2(ie, GTPIE_FL_C, 0, &pdp->flrc); + gtpie_gettv4(ie, GTPIE_CHARGING_ID, 0, &pdp->cid); + gtpie_gettlv(ie, GTPIE_EUA, 0, &pdp->eua.l, + &pdp->eua.v, sizeof(pdp->eua.v)); + gtpie_gettlv(ie, GTPIE_GSN_ADDR, 0, &pdp->gsnrc.l, + &pdp->gsnrc.v, sizeof(pdp->gsnrc.v)); + 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, cbp); + return 0; /* Succes */ + } +} /* API: Send Delete PDP Context Request */ int gtp_delete_context_req(struct gsn_t *gsn, struct pdp_t *pdp, void *cbp, - int teardown) { - union gtp_packet packet; - unsigned int length = get_default_gtp(pdp->version, GTP_DELETE_PDP_REQ, &packet); - struct in_addr addr; - struct pdp_t *linked_pdp; - struct pdp_t *secondary_pdp; - int n; - int count = 0; - - if (gsna2in_addr(&addr, &pdp->gsnrc)) { - gsn->err_address++; - gtp_err(LOG_ERR, __FILE__, __LINE__, "GSN address conversion failed"); - return EOF; - } - - if (pdp_getgtp1(&linked_pdp, pdp->teic_own)) { - gtp_err(LOG_ERR, __FILE__, __LINE__, "Unknown linked PDP context"); - return EOF; - } - - if (!teardown) { - for (n=0; n< PDP_MAXNSAPI; n++) - if (linked_pdp->secondary_tei[n]) count++; - if (count <= 1) { - gtp_err(LOG_ERR, __FILE__, __LINE__, - "Must use teardown for last context"); - return EOF; - } - } - - if (pdp->version == 1) { - if (teardown) - gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_TEARDOWN, - 0xff); - - gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI, - pdp->nsapi); - } - - gtp_req(gsn, pdp->version, pdp, &packet, length, &addr, cbp); - - if (teardown) { /* Remove all contexts */ - for (n=0; n< PDP_MAXNSAPI; n++) { - if (linked_pdp->secondary_tei[n]) { - if (pdp_getgtp1(&secondary_pdp, linked_pdp->secondary_tei[n])) { - gtp_err(LOG_ERR, __FILE__, __LINE__, "Unknown secondary PDP context"); - return EOF; - } - if (linked_pdp != secondary_pdp) { - if (gsn->cb_delete_context) gsn->cb_delete_context(secondary_pdp); - pdp_freepdp(secondary_pdp); - } - } - } - if (gsn->cb_delete_context) gsn->cb_delete_context(linked_pdp); - pdp_freepdp(linked_pdp); - } - else { - if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - if (pdp == linked_pdp) { - linked_pdp->secondary_tei[pdp->nsapi & 0xf0] = 0; - linked_pdp->nodata = 1; - } - else - pdp_freepdp(pdp); - } - - return 0; + int teardown) +{ + union gtp_packet packet; + unsigned int length = + get_default_gtp(pdp->version, GTP_DELETE_PDP_REQ, &packet); + struct in_addr addr; + struct pdp_t *linked_pdp; + struct pdp_t *secondary_pdp; + int n; + int count = 0; + + if (gsna2in_addr(&addr, &pdp->gsnrc)) { + gsn->err_address++; + gtp_err(LOG_ERR, __FILE__, __LINE__, + "GSN address conversion failed"); + return EOF; + } + + if (pdp_getgtp1(&linked_pdp, pdp->teic_own)) { + gtp_err(LOG_ERR, __FILE__, __LINE__, + "Unknown linked PDP context"); + return EOF; + } + + if (!teardown) { + for (n = 0; n < PDP_MAXNSAPI; n++) + if (linked_pdp->secondary_tei[n]) + count++; + if (count <= 1) { + gtp_err(LOG_ERR, __FILE__, __LINE__, + "Must use teardown for last context"); + return EOF; + } + } + + if (pdp->version == 1) { + if (teardown) + gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_TEARDOWN, + 0xff); + + gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_NSAPI, pdp->nsapi); + } + + gtp_req(gsn, pdp->version, pdp, &packet, length, &addr, cbp); + + if (teardown) { /* Remove all contexts */ + for (n = 0; n < PDP_MAXNSAPI; n++) { + if (linked_pdp->secondary_tei[n]) { + if (pdp_getgtp1 + (&secondary_pdp, + linked_pdp->secondary_tei[n])) { + gtp_err(LOG_ERR, __FILE__, __LINE__, + "Unknown secondary PDP context"); + return EOF; + } + if (linked_pdp != secondary_pdp) { + if (gsn->cb_delete_context) + gsn->cb_delete_context + (secondary_pdp); + pdp_freepdp(secondary_pdp); + } + } + } + if (gsn->cb_delete_context) + gsn->cb_delete_context(linked_pdp); + pdp_freepdp(linked_pdp); + } else { + if (gsn->cb_delete_context) + gsn->cb_delete_context(pdp); + if (pdp == linked_pdp) { + linked_pdp->secondary_tei[pdp->nsapi & 0xf0] = 0; + linked_pdp->nodata = 1; + } else + pdp_freepdp(pdp); + } + + return 0; } /* Send Delete PDP Context Response */ int gtp_delete_pdp_resp(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, - void *pack, unsigned len, - struct pdp_t *pdp, struct pdp_t *linked_pdp, + void *pack, unsigned len, + struct pdp_t *pdp, struct pdp_t *linked_pdp, uint8_t cause, int teardown) { - union gtp_packet packet; - struct pdp_t *secondary_pdp; - unsigned int length = get_default_gtp(version, GTP_DELETE_PDP_RSP, &packet); - int n; - - gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_CAUSE, cause); - - gtp_resp(version, gsn, pdp, &packet, length, peer, fd, - get_seq(pack), get_tid(pack)); - - if (cause == GTPCAUSE_ACC_REQ) { - if ((teardown) || (version == 0)) { /* Remove all contexts */ - for (n=0; n< PDP_MAXNSAPI; n++) { - if (linked_pdp->secondary_tei[n]) { - if (pdp_getgtp1(&secondary_pdp, linked_pdp->secondary_tei[n])) { - gtp_err(LOG_ERR, __FILE__, __LINE__, - "Unknown secondary PDP context"); - return EOF; - } - if (linked_pdp != secondary_pdp) { - if (gsn->cb_delete_context) gsn->cb_delete_context(secondary_pdp); - pdp_freepdp(secondary_pdp); - } - } - } - if (gsn->cb_delete_context) gsn->cb_delete_context(linked_pdp); - pdp_freepdp(linked_pdp); - } - else { /* Remove only current context */ - if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - if (pdp == linked_pdp) { - linked_pdp->secondary_tei[pdp->nsapi & 0xf0] = 0; - linked_pdp->nodata = 1; - } - else - pdp_freepdp(pdp); - } - } /* if (cause == GTPCAUSE_ACC_REQ) */ - - return 0; + union gtp_packet packet; + struct pdp_t *secondary_pdp; + unsigned int length = + get_default_gtp(version, GTP_DELETE_PDP_RSP, &packet); + int n; + + gtpie_tv1(&packet, &length, GTP_MAX, GTPIE_CAUSE, cause); + + gtp_resp(version, gsn, pdp, &packet, length, peer, fd, + get_seq(pack), get_tid(pack)); + + if (cause == GTPCAUSE_ACC_REQ) { + if ((teardown) || (version == 0)) { /* Remove all contexts */ + for (n = 0; n < PDP_MAXNSAPI; n++) { + if (linked_pdp->secondary_tei[n]) { + if (pdp_getgtp1 + (&secondary_pdp, + linked_pdp->secondary_tei[n])) { + gtp_err(LOG_ERR, __FILE__, + __LINE__, + "Unknown secondary PDP context"); + return EOF; + } + if (linked_pdp != secondary_pdp) { + if (gsn->cb_delete_context) + gsn->cb_delete_context + (secondary_pdp); + pdp_freepdp(secondary_pdp); + } + } + } + if (gsn->cb_delete_context) + gsn->cb_delete_context(linked_pdp); + pdp_freepdp(linked_pdp); + } else { /* Remove only current context */ + if (gsn->cb_delete_context) + gsn->cb_delete_context(pdp); + if (pdp == linked_pdp) { + linked_pdp->secondary_tei[pdp->nsapi & 0xf0] = + 0; + linked_pdp->nodata = 1; + } else + pdp_freepdp(pdp); + } + } + /* if (cause == GTPCAUSE_ACC_REQ) */ + return 0; } /* Handle Delete PDP Context Request */ int gtp_delete_pdp_ind(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, - void *pack, unsigned len) { - struct pdp_t *pdp = NULL; - struct pdp_t *linked_pdp = NULL; - union gtpie_member* ie[GTPIE_SIZE]; - - uint16_t seq = get_seq(pack); - int hlen = get_hlen(pack); - - uint8_t nsapi; - uint8_t teardown = 0; - int n; - int count = 0; - - /* Is this a dublicate ? */ - if(!gtp_dublicate(gsn, version, peer, seq)) { - return 0; /* We allready send off response once */ - } - - /* Find the linked context in question */ - if (pdp_getgtp1(&linked_pdp, get_tei(pack))) { - gsn->err_unknownpdp++; - gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len, - "Unknown PDP context"); - return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len, NULL, NULL, - GTPCAUSE_NON_EXIST, teardown); - } - - /* If version 0 this is also the secondary context */ - if (version == 0) - pdp = linked_pdp; - - /* Decode information elements */ - 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, fd, pack, len, NULL, NULL, - GTPCAUSE_INVALID_MESSAGE, teardown); - } - - 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, NULL, - GTPCAUSE_MAN_IE_MISSING, teardown); - } - - /* Find the context in question */ - if (pdp_getgtp1(&pdp, linked_pdp->secondary_tei[nsapi & 0x0f])) { - gsn->err_unknownpdp++; - gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len, - "Unknown PDP context"); - return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len, NULL, NULL, - GTPCAUSE_NON_EXIST, teardown); - } - - /* Teardown (conditional) */ - gtpie_gettv1(ie, GTPIE_TEARDOWN, 0, &teardown); - - if (!teardown) { - for (n=0; n< PDP_MAXNSAPI; n++) - if (linked_pdp->secondary_tei[n]) count++; - if (count <= 1) { - return 0; /* 29.060 7.3.5 Ignore message */ - } - } - } - - return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len, - pdp, linked_pdp, GTPCAUSE_ACC_REQ, teardown); -} + void *pack, unsigned len) +{ + struct pdp_t *pdp = NULL; + struct pdp_t *linked_pdp = NULL; + union gtpie_member *ie[GTPIE_SIZE]; + uint16_t seq = get_seq(pack); + int hlen = get_hlen(pack); + + uint8_t nsapi; + uint8_t teardown = 0; + int n; + int count = 0; + + /* Is this a dublicate ? */ + if (!gtp_dublicate(gsn, version, peer, seq)) { + return 0; /* We allready send off response once */ + } + + /* Find the linked context in question */ + if (pdp_getgtp1(&linked_pdp, get_tei(pack))) { + gsn->err_unknownpdp++; + gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len, + "Unknown PDP context"); + return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len, + NULL, NULL, GTPCAUSE_NON_EXIST, + teardown); + } + + /* If version 0 this is also the secondary context */ + if (version == 0) + pdp = linked_pdp; + + /* Decode information elements */ + 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, fd, pack, + len, NULL, NULL, + GTPCAUSE_INVALID_MESSAGE, + teardown); + } + + 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, NULL, + GTPCAUSE_MAN_IE_MISSING, + teardown); + } + + /* Find the context in question */ + if (pdp_getgtp1(&pdp, linked_pdp->secondary_tei[nsapi & 0x0f])) { + gsn->err_unknownpdp++; + gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, + len, "Unknown PDP context"); + return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, + len, NULL, NULL, + GTPCAUSE_NON_EXIST, + teardown); + } + + /* Teardown (conditional) */ + gtpie_gettv1(ie, GTPIE_TEARDOWN, 0, &teardown); + + if (!teardown) { + for (n = 0; n < PDP_MAXNSAPI; n++) + if (linked_pdp->secondary_tei[n]) + count++; + if (count <= 1) { + return 0; /* 29.060 7.3.5 Ignore message */ + } + } + } + + return gtp_delete_pdp_resp(gsn, version, peer, fd, pack, len, + pdp, linked_pdp, GTPCAUSE_ACC_REQ, teardown); +} /* Handle Delete PDP Context Response */ int gtp_delete_pdp_conf(struct gsn_t *gsn, int version, - struct sockaddr_in *peer, - void *pack, unsigned len) { - union gtpie_member *ie[GTPIE_SIZE]; - uint8_t cause; - void *cbp = NULL; - uint8_t type = 0; - int hlen = get_hlen(pack); - - /* Remove packet from queue */ - if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp)) return EOF; - - /* Decode information elements */ - 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; - } - - /* 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, NULL, cbp); - return EOF; - } - - /* Check the cause value (again) */ - if ((GTPCAUSE_ACC_REQ != cause) && (GTPCAUSE_NON_EXIST != cause)) { - gsn->err_cause++; - gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len, - "Unexpected cause value received: %d", cause); - if (gsn->cb_conf) gsn->cb_conf(type, cause, NULL, cbp); - return EOF; - } - - /* Callback function to notify application */ - if (gsn->cb_conf) gsn->cb_conf(type, cause, NULL, cbp); - - return 0; + struct sockaddr_in *peer, void *pack, unsigned len) +{ + union gtpie_member *ie[GTPIE_SIZE]; + uint8_t cause; + void *cbp = NULL; + uint8_t type = 0; + int hlen = get_hlen(pack); + + /* Remove packet from queue */ + if (gtp_conf(gsn, version, peer, pack, len, &type, &cbp)) + return EOF; + + /* Decode information elements */ + 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; + } + + /* 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, NULL, cbp); + return EOF; + } + + /* Check the cause value (again) */ + if ((GTPCAUSE_ACC_REQ != cause) && (GTPCAUSE_NON_EXIST != cause)) { + gsn->err_cause++; + gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len, + "Unexpected cause value received: %d", cause); + if (gsn->cb_conf) + gsn->cb_conf(type, cause, NULL, cbp); + return EOF; + } + + /* Callback function to notify application */ + if (gsn->cb_conf) + gsn->cb_conf(type, cause, NULL, cbp); + + return 0; } /* Send Error Indication (response to a GPDU message */ @@ -2485,89 +2624,90 @@ int gtp_error_ind_resp(struct gsn_t *gsn, int version, struct sockaddr_in *peer, int fd, void *pack, unsigned len) { - union gtp_packet packet; - unsigned int length = get_default_gtp(version, GTP_ERROR, &packet); - - return gtp_resp(version, gsn, NULL, &packet, length, peer, fd, - get_seq(pack), get_tid(pack)); + union gtp_packet packet; + unsigned int length = get_default_gtp(version, GTP_ERROR, &packet); + + return gtp_resp(version, gsn, NULL, &packet, length, peer, fd, + get_seq(pack), get_tid(pack)); } /* Handle Error Indication */ int gtp_error_ind_conf(struct gsn_t *gsn, int version, - struct sockaddr_in *peer, - void *pack, unsigned len) { - struct pdp_t *pdp; - - /* Find the context in question */ - if (pdp_tidget(&pdp, ((union gtp_packet*)pack)->gtp0.h.tid)) { - gsn->err_unknownpdp++; - gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len, - "Unknown PDP context"); - return EOF; - } - - gsn->err_unknownpdp++; /* TODO: Change counter */ - gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len, - "Received Error Indication"); - - if (gsn->cb_delete_context) gsn->cb_delete_context(pdp); - pdp_freepdp(pdp); - return 0; + struct sockaddr_in *peer, void *pack, unsigned len) +{ + struct pdp_t *pdp; + + /* Find the context in question */ + if (pdp_tidget(&pdp, ((union gtp_packet *)pack)->gtp0.h.tid)) { + gsn->err_unknownpdp++; + gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len, + "Unknown PDP context"); + return EOF; + } + + gsn->err_unknownpdp++; /* TODO: Change counter */ + gtp_errpack(LOG_ERR, __FILE__, __LINE__, peer, pack, len, + "Received Error Indication"); + + if (gsn->cb_delete_context) + gsn->cb_delete_context(pdp); + pdp_freepdp(pdp); + return 0; } int gtp_gpdu_ind(struct gsn_t *gsn, int version, - 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 (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, fd, pack, len); - } - - /* Callback function */ - if (gsn->cb_data_ind !=0) - return gsn->cb_data_ind(pdp, pack+hlen, len-hlen); - - return 0; -} + 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 (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, fd, pack, len); + } + /* Callback function */ + 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 @@ -2576,505 +2716,548 @@ int gtp_gpdu_ind(struct gsn_t *gsn, int version, * TODO: Need to decide on return values! */ int gtp_decaps0(struct gsn_t *gsn) { - unsigned char buffer[PACKET_MAX]; - struct sockaddr_in peer; - size_t 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->fd0, F_SETFL, O_NONBLOCK)) { - gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()"); - return -1; - } - peerlen = sizeof(peer); - if ((status = - recvfrom(gsn->fd0, buffer, sizeof(buffer), 0, - (struct sockaddr *) &peer, &peerlen)) < 0 ) { - if (errno == EAGAIN) return 0; - gsn->err_readfrom++; - 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; - } - - /* 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 gtp0_header *) (buffer); - - /* 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_req(gsn, 0, &peer, gsn->fd0, buffer, status); /* 29.60: 11.1.1 */ - continue; - } - - /* Check length of gtp0 packet */ - 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) || - (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, 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; - } - } + unsigned char buffer[PACKET_MAX]; + struct sockaddr_in peer; + size_t 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->fd0, F_SETFL, O_NONBLOCK)) { + gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()"); + return -1; + } + peerlen = sizeof(peer); + if ((status = + recvfrom(gsn->fd0, buffer, sizeof(buffer), 0, + (struct sockaddr *)&peer, &peerlen)) < 0) { + if (errno == EAGAIN) + return 0; + gsn->err_readfrom++; + 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; + } + + /* 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 gtp0_header *)(buffer); + + /* 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_req(gsn, 0, &peer, gsn->fd0, buffer, status); /* 29.60: 11.1.1 */ + continue; + } + + /* Check length of gtp0 packet */ + 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) || + (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, 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]; - struct sockaddr_in peer; - size_t peerlen; - int status; - struct gtp1_header_short *pheader; - int version = 1; /* TODO 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(fd, F_SETFL, O_NONBLOCK)) { - gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()"); - return -1; - } - peerlen = sizeof(peer); - if ((status = - recvfrom(fd, buffer, sizeof(buffer), 0, - (struct sockaddr *) &peer, &peerlen)) < 0 ) { - 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", fd, (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, version, &peer, fd, 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 */ - } - - /* Check for extension headers */ - /* TODO: We really should cycle through the headers and determine */ - /* if any have the comprehension required flag set */ - if (((pheader->flags & 0x04) != 0x00)) { - gsn->unsup++; - gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status, - "Unsupported extension header"); - gtp_extheader_req(gsn, version, &peer, fd, buffer, status); - - continue; - } - - 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, 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_SUPP_EXT_HEADER: - gtp_extheader_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; - default: - gsn->unknown++; - gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status, - "Unknown GTP message type received"); - break; - } - } + unsigned char buffer[PACKET_MAX]; + struct sockaddr_in peer; + size_t peerlen; + int status; + struct gtp1_header_short *pheader; + int version = 1; /* TODO 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(fd, F_SETFL, O_NONBLOCK)) { + gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()"); + return -1; + } + peerlen = sizeof(peer); + if ((status = + recvfrom(fd, buffer, sizeof(buffer), 0, + (struct sockaddr *)&peer, &peerlen)) < 0) { + 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", + fd, (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, version, &peer, fd, 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 */ + } + + /* Check for extension headers */ + /* TODO: We really should cycle through the headers and determine */ + /* if any have the comprehension required flag set */ + if (((pheader->flags & 0x04) != 0x00)) { + gsn->unsup++; + gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, + status, "Unsupported extension header"); + gtp_extheader_req(gsn, version, &peer, fd, buffer, + status); + + continue; + } + + 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, 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_SUPP_EXT_HEADER: + gtp_extheader_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; + 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]; - struct sockaddr_in peer; - size_t 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 */ - } - - /* Check for extension headers */ - /* TODO: We really should cycle through the headers and determine */ - /* if any have the comprehension required flag set */ - if (((pheader->flags & 0x04) != 0x00)) { - gsn->unsup++; - gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status, - "Unsupported extension header"); - gtp_extheader_req(gsn, version, &peer, fd, buffer, status); - - continue; - } - - 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_SUPP_EXT_HEADER: - gtp_extheader_ind(gsn, &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, fd, buffer, status); - break; - default: - gsn->unknown++; - gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, status, - "Unknown GTP message type received"); - break; - } - } + unsigned char buffer[PACKET_MAX]; + struct sockaddr_in peer; + size_t 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 */ + } + + /* Check for extension headers */ + /* TODO: We really should cycle through the headers and determine */ + /* if any have the comprehension required flag set */ + if (((pheader->flags & 0x04) != 0x00)) { + gsn->unsup++; + gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, + status, "Unsupported extension header"); + gtp_extheader_req(gsn, version, &peer, fd, buffer, + status); + + continue; + } + + 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_SUPP_EXT_HEADER: + gtp_extheader_ind(gsn, &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, fd, buffer, status); + break; + default: + gsn->unknown++; + gtp_errpack(LOG_ERR, __FILE__, __LINE__, &peer, buffer, + status, + "Unknown GTP message type received"); + break; + } + } } -int gtp_data_req(struct gsn_t *gsn, struct pdp_t* pdp, - void *pack, unsigned len) +int gtp_data_req(struct gsn_t *gsn, struct pdp_t *pdp, void *pack, unsigned len) { - union gtp_packet packet; - struct sockaddr_in addr; - int fd; - int length; + union gtp_packet packet; + struct sockaddr_in addr; + int fd; + int length; - memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; #if defined(__FreeBSD__) || defined(__APPLE__) - addr.sin_len = sizeof(addr); + addr.sin_len = sizeof(addr); #endif - memcpy(&addr.sin_addr, pdp->gsnru.v,pdp->gsnru.l); /* TODO range check */ - - 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 & 0x0fffffffffffffffull) + ((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: %d > %d", len, - sizeof (union gtp_packet) - sizeof(struct gtp0_header)); - 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++); - packet.gtp1l.h.tei = hton32(pdp->teid_gn); - - if (len > sizeof (union gtp_packet) - sizeof(struct gtp1_header_long)) { - gsn->err_memcpy++; - gtp_err(LOG_ERR, __FILE__, __LINE__, - "Memcpy failed: %d > %d", len, - sizeof (union gtp_packet) - sizeof(struct gtp0_header)); - return EOF; - } - memcpy(packet.gtp1l.p, pack, len); /* TODO Should be avoided! */ - } - else { - gtp_err(LOG_ERR, __FILE__, __LINE__, - "Unknown version"); - return EOF; - } - - if (fcntl(fd, F_SETFL, 0)) { - gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()"); - return -1; - } - - 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", fd, (unsigned long) &packet, GTP0_HEADER_SIZE+len, strerror(errno)); - return EOF; - } - return 0; -} + memcpy(&addr.sin_addr, pdp->gsnru.v, pdp->gsnru.l); /* TODO range check */ + + 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 & 0x0fffffffffffffffull) + + ((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: %d > %d", len, + sizeof(union gtp_packet) - + sizeof(struct gtp0_header)); + 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++); + packet.gtp1l.h.tei = hton32(pdp->teid_gn); + + if (len > + sizeof(union gtp_packet) - + sizeof(struct gtp1_header_long)) { + gsn->err_memcpy++; + gtp_err(LOG_ERR, __FILE__, __LINE__, + "Memcpy failed: %d > %d", len, + sizeof(union gtp_packet) - + sizeof(struct gtp0_header)); + return EOF; + } + memcpy(packet.gtp1l.p, pack, len); /* TODO Should be avoided! */ + } else { + gtp_err(LOG_ERR, __FILE__, __LINE__, "Unknown version"); + return EOF; + } + if (fcntl(fd, F_SETFL, 0)) { + gtp_err(LOG_ERR, __FILE__, __LINE__, "fnctl()"); + return -1; + } + + 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", fd, + (unsigned long)&packet, GTP0_HEADER_SIZE + len, + strerror(errno)); + return EOF; + } + return 0; +} /* *********************************************************** * Conversion functions *************************************************************/ -int char2ul_t(char* src, struct ul_t dst) { - dst.l = strlen(src)+1; - dst.v = malloc(dst.l); - dst.v[0] = dst.l - 1; - memcpy(&dst.v[1], src, dst.v[0]); - return 0; +int char2ul_t(char *src, struct ul_t dst) +{ + dst.l = strlen(src) + 1; + dst.v = malloc(dst.l); + dst.v[0] = dst.l - 1; + memcpy(&dst.v[1], src, dst.v[0]); + return 0; } /* *********************************************************** @@ -3090,40 +3273,40 @@ int char2ul_t(char* src, struct ul_t dst) { * port number. *************************************************************/ -int ipv42eua(struct ul66_t *eua, struct in_addr *src) { - eua->v[0] = 0xf1; /* IETF */ - eua->v[1] = 0x21; /* IPv4 */ - if (src) { - eua->l = 6; - memcpy(&eua->v[2], src, 4); - } - else - { - eua->l = 2; - } - return 0; +int ipv42eua(struct ul66_t *eua, struct in_addr *src) +{ + eua->v[0] = 0xf1; /* IETF */ + eua->v[1] = 0x21; /* IPv4 */ + if (src) { + eua->l = 6; + memcpy(&eua->v[2], src, 4); + } else { + eua->l = 2; + } + return 0; } -int eua2ipv4(struct in_addr *dst, struct ul66_t *eua) { - if ((eua->l != 6) || - (eua->v[0] != 0xf1) || - (eua->v[1] = 0x21)) - return -1; /* Not IPv4 address*/ - memcpy(dst, &eua->v[2], 4); - return 0; +int eua2ipv4(struct in_addr *dst, struct ul66_t *eua) +{ + if ((eua->l != 6) || (eua->v[0] != 0xf1) || (eua->v[1] = 0x21)) + return -1; /* Not IPv4 address */ + memcpy(dst, &eua->v[2], 4); + return 0; } -int gsna2in_addr(struct in_addr *dst, struct ul16_t *gsna) { - memset(dst, 0, sizeof(struct in_addr)); - if (gsna->l != 4) return EOF; /* Return if not IPv4 */ - memcpy(dst, gsna->v, gsna->l); - return 0; +int gsna2in_addr(struct in_addr *dst, struct ul16_t *gsna) +{ + memset(dst, 0, sizeof(struct in_addr)); + if (gsna->l != 4) + return EOF; /* Return if not IPv4 */ + memcpy(dst, gsna->v, gsna->l); + return 0; } -int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src) { - memset(gsna, 0, sizeof(struct ul16_t)); - gsna->l = 4; - memcpy(gsna->v, src, gsna->l); - return 0; +int in_addr2gsna(struct ul16_t *gsna, struct in_addr *src) +{ + memset(gsna, 0, sizeof(struct ul16_t)); + gsna->l = 4; + memcpy(gsna->v, src, gsna->l); + return 0; } - |