583 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			583 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* frames.c - handle incoming frames, prefill outgoing frames, parse TLVs etc. */
 | |
| 
 | |
| /* Copyright (C) 1999 Heikki Vatiainen hessu@cs.tut.fi */
 | |
| 
 | |
| /* Functions for handling LANE control frames used when joining an
 | |
|  * ELAN are in lec.c
 | |
|  */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <string.h>
 | |
| #include <stdlib.h>
 | |
| 
 | |
| #include <atm.h>
 | |
| #include <linux/atmlec.h>
 | |
| #include <atmd.h>
 | |
| 
 | |
| #include "conn.h"
 | |
| #include "lec.h"
 | |
| #include "frames.h"
 | |
| #include "display.h"
 | |
| #include "kernel.h"
 | |
| 
 | |
| #define COMPONENT "frames.c"
 | |
| 
 | |
| static uint32_t transaction_id = 0;
 | |
| static void extract_tlv_value(uint16_t opcode, uint32_t type, unsigned char *tlvp, int len);
 | |
| static void handle_x5(uint16_t opcode);
 | |
| 
 | |
| /* Initializes LANE Control frame of type 'type'
 | |
|  */
 | |
| void prefill_frame(void *buff, uint16_t type)
 | |
| {
 | |
|         struct frame_hdr *header;
 | |
|         
 | |
|         memset(buff, 0, sizeof(struct ctrl_frame));
 | |
|         header = (struct frame_hdr *)buff;
 | |
|         header->marker   = htons(0xff00);
 | |
|         header->protocol = 0x01;
 | |
|         header->version  = 0x01;
 | |
|         header->opcode   = htons(type);
 | |
|         header->status   = htons(0x0000);
 | |
|         header->tran_id  = htonl(transaction_id);
 | |
|         header->lec_id   = htons(lec_params.c14_lec_id);
 | |
|         header->flags    = htons(0x0000);
 | |
|         
 | |
|         transaction_id++;
 | |
|         
 | |
|         return;
 | |
| }
 | |
| 
 | |
| /* Validates incoming Control frames except READY_IND and
 | |
|  * READY_QUERY which do not start with the common header.
 | |
|  * Also calls display_frame() to print out conforming frames.
 | |
|  * Returns < 0 for error
 | |
|  */
 | |
| int validate_frame(unsigned char *buff, int size)
 | |
| {
 | |
|         struct ready_frame *hdr; /* Ready is the shortest possible */
 | |
| 
 | |
|         if (size < sizeof(struct ready_frame)) {
 | |
|                 diag(COMPONENT, DIAG_DEBUG, "short frame, size %d\n", size);
 | |
|                 return -1;
 | |
|         }
 | |
| 
 | |
|         hdr = (struct ready_frame *)buff;
 | |
|         if (hdr->marker   != htons(0xff00) ||
 | |
|             hdr->protocol != 0x01 ||
 | |
|             hdr->version  != 0x01)
 | |
|                 return -1;
 | |
| 
 | |
|         /* READY_* frames are shorter than others */
 | |
|         if (hdr->opcode == htons(READY_QUERY) ||
 | |
|             hdr->opcode == htons(READY_IND)) {
 | |
|                 diag(COMPONENT, DIAG_DEBUG, "Received a %s\n", opcode2text(hdr->opcode));
 | |
|                 return 0;
 | |
|         }
 | |
| 
 | |
|         if (size < sizeof(struct ctrl_frame)) {
 | |
|                 diag(COMPONENT, DIAG_DEBUG, "short frame, size %d\n", size);
 | |
|                 return -1;
 | |
|         }
 | |
| 
 | |
|         display_frame(buff);
 | |
| 
 | |
|         return 0;
 | |
| }
 | |
| 
 | |
| /* Handle incoming LE_FLUSH_REQUEST frames.
 | |
|  */
 | |
| static void handle_flush_req(struct ctrl_frame *f)
 | |
| {
 | |
|         if (memcmp(lec_params.c1n_my_atm_addr, f->target_atm_addr, ATM_ESA_LEN) != 0)
 | |
|                 return;
 | |
|         f->header.opcode = htons(LE_FLUSH_RSP);
 | |
|         if (send_frame(lec_params.ctrl_direct, f, sizeof(struct ctrl_frame)) < 0)
 | |
|                 diag(COMPONENT, DIAG_DEBUG, "could not send LE_FLUSH_RESPONSE\n");
 | |
| 
 | |
|         return;
 | |
| }
 | |
| 
 | |
| /* Handle incoming LE_FLUSH_RESPONSE frames.
 | |
|  */
 | |
| static void handle_flush_rsp(struct ctrl_frame *f)
 | |
| {
 | |
|         struct atmlec_msg msg;
 | |
| 
 | |
|         if (f->header.lec_id != htons(lec_params.c14_lec_id)) {
 | |
|                 diag(COMPONENT, DIAG_DEBUG, "Wrong lec_id, ignoring\n");
 | |
|                 return;
 | |
|         }
 | |
| 
 | |
|         memset(&msg, 0, sizeof(struct atmlec_msg));
 | |
|         msg.type = l_flush_complete;
 | |
|         msg.content.normal.flag = ntohl(f->header.tran_id);
 | |
|         msg_to_kernel(&msg, sizeof(struct atmlec_msg));
 | |
|         
 | |
|         return;
 | |
| }
 | |
| 
 | |
| /* Handle incoming READY_QUERY frames.
 | |
|  */
 | |
| static void handle_ready_query(Conn_t *conn, struct ready_frame *f)
 | |
| {
 | |
|         f->opcode = htons(READY_IND);
 | |
|         send_frame(conn, f, sizeof(struct ready_frame));
 | |
| 
 | |
|         return;
 | |
| }
 | |
| 
 | |
| /* Helper for handle_le_arp_req.
 | |
|  * If the target_lan_dst was not our MAC address, try to
 | |
|  * see if the bridging table in the kernel knows about it.
 | |
|  * Returns < 0 for serious error
 | |
|  */
 | |
| static int check_bridge(struct ctrl_frame *frame, int size)
 | |
| {
 | |
|         struct atmlec_msg msg;
 | |
| 
 | |
|         if (lec_params.c4_proxy_flag == 0)
 | |
|                 return 0;
 | |
| 
 | |
|         memset(&msg, 0, sizeof(struct atmlec_msg));
 | |
|         msg.type = l_should_bridge;
 | |
|         memcpy(msg.content.proxy.mac_addr, frame->target_lan_dst.mac, ETH_ALEN);
 | |
|         memcpy(msg.content.proxy.atm_addr, frame->src_atm_addr, ATM_ESA_LEN);
 | |
|         msg.content.proxy.tran_id = frame->header.tran_id;
 | |
|         msg.content.proxy.lec_id = frame->header.lec_id;
 | |
| 
 | |
|         return msg_to_kernel(&msg, sizeof(struct atmlec_msg));
 | |
| }
 | |
| 
 | |
| /* Handles incoming LE_ARP_REQ and targetless LE_ARP_REQ.
 | |
|  * See LANEv2, 7.1.5 and 7.1.30
 | |
|  * Returns < 0 for serious error
 | |
|  */
 | |
| static int handle_le_arp_req(struct ctrl_frame *frame, int size)
 | |
| {
 | |
|         int sizeoftlvs, sizeofrsp, retval;
 | |
|         struct ctrl_frame *rsp;
 | |
|         struct atmlec_msg *msg;
 | |
| 
 | |
|         if (frame->header.lec_id == htons(lec_params.c14_lec_id)) {
 | |
|                 diag(COMPONENT, DIAG_DEBUG, "Ignoring own LE_ARP_REQUEST\n");
 | |
|                 return 0;
 | |
|         }
 | |
| 
 | |
|         retval = 0;
 | |
|         if (frame->target_lan_dst.tag == htons(LAN_DST_MAC_ADDR)) {
 | |
|                 if (memcmp(frame->target_lan_dst.mac, lec_params.c6_mac_addr, ETH_ALEN) != 0)
 | |
|                         return (check_bridge(frame, size)); /* target was not us */
 | |
|                 sizeofrsp = sizeof(struct ctrl_frame) + lec_params.sizeoftlvs;
 | |
|                 rsp = (struct ctrl_frame *)malloc(sizeofrsp);
 | |
|                 if (rsp == NULL) return 0;
 | |
|                 memcpy(rsp, frame, sizeof(struct ctrl_frame));
 | |
|                 rsp->header.opcode = htons(LE_ARP_RSP);
 | |
|                 memcpy(rsp->target_atm_addr, lec_params.c1n_my_atm_addr, ATM_ESA_LEN);
 | |
|                 rsp->num_tlvs = lec_params.num_tlvs;
 | |
|                 if (lec_params.num_tlvs > 0)
 | |
|                         memcpy(rsp + 1, lec_params.tlvs, lec_params.sizeoftlvs);
 | |
| 
 | |
|                 retval = send_frame(lec_params.ctrl_direct, rsp, sizeofrsp);
 | |
|                 free(rsp);
 | |
|         } else if (frame->target_lan_dst.tag == htons(LAN_DST_NOT_PRESENT) &&
 | |
|                    lec_params.c29_v2_capable) {
 | |
|                 sizeoftlvs = size - sizeof(struct ctrl_frame);
 | |
|                 msg = (struct atmlec_msg *)malloc(sizeof(struct atmlec_msg) + sizeoftlvs);
 | |
|                 if (msg == NULL) return -1;
 | |
|                 memset(msg, 0, sizeof(struct atmlec_msg));
 | |
|                 msg->type = l_arp_update;
 | |
|                 memcpy(msg->content.normal.mac_addr, frame->src_lan_dst.mac, ETH_ALEN);
 | |
|                 memcpy(msg->content.normal.atm_addr, frame->src_atm_addr, ATM_ESA_LEN);
 | |
|                 msg->content.normal.flag = (frame->header.flags & REMOTE_ADDRESS) ? 1 : 0;
 | |
|                 msg->content.normal.targetless_le_arp = 1;
 | |
|                 msg->sizeoftlvs = sizeoftlvs;
 | |
|                 if (sizeoftlvs > 0) memcpy(msg + 1, frame + 1, sizeoftlvs);
 | |
| 
 | |
|                 retval = msg_to_kernel(msg, sizeof(struct atmlec_msg) + sizeoftlvs);
 | |
|                 free(msg);
 | |
|         }
 | |
|         
 | |
|         return retval;
 | |
| }
 | |
| 
 | |
| /* Handles incoming LE_NARP_REQUESTS frames.
 | |
|  * Mandatory only in LANEv2. If we are LANEv1, we'll just ignore these.
 | |
|  * See LANEv2, 7.1.31-35 LE_NARP_REQUEST/RESPONSE.
 | |
|  * For no-source, i.e. no source ATM address, we remove the LE_ARP cache entry.
 | |
|  * If the source is non-zero, we first remove the entry
 | |
|  * and then add the new entry in the LE_ARP cache.
 | |
|  * Returns < 0 for serious error.
 | |
|  */
 | |
| static int handle_narp_req(struct ctrl_frame *frame, int size)
 | |
| {
 | |
|         int sizeoftlvs, no_source = 0, retval;
 | |
|         struct atmlec_msg *msg;
 | |
|         unsigned char empty[ATM_ESA_LEN];
 | |
| 
 | |
|         if (frame->header.lec_id == htons(lec_params.c14_lec_id) ||
 | |
|             lec_params.c29_v2_capable == 0) {
 | |
|                 diag(COMPONENT, DIAG_DEBUG, "Ignoring LE_NARP_REQUEST\n");
 | |
|                 return 0;
 | |
|         }
 | |
| 
 | |
|         memset(empty, 0, ATM_ESA_LEN);
 | |
|         if (memcmp(empty, frame->src_atm_addr, ATM_ESA_LEN) == 0)
 | |
|                 no_source = 1;
 | |
| 
 | |
|         sizeoftlvs = size - sizeof(struct ctrl_frame);
 | |
|         msg = (struct atmlec_msg *)malloc(sizeof(struct atmlec_msg) + sizeoftlvs);
 | |
|         if (msg == NULL) return -1;
 | |
|         memset(msg, 0, sizeof(struct atmlec_msg));
 | |
|         msg->type = l_narp_req;
 | |
|         memcpy(msg->content.normal.mac_addr, frame->src_lan_dst.mac, ETH_ALEN);
 | |
|         memcpy(msg->content.normal.atm_addr, frame->src_atm_addr, ATM_ESA_LEN);
 | |
|         msg->content.normal.flag = (frame->header.flags & REMOTE_ADDRESS) ? 1 : 0;
 | |
|         msg->content.normal.no_source_le_narp = no_source;
 | |
|         msg->sizeoftlvs = sizeoftlvs;
 | |
|         if (sizeoftlvs > 0) memcpy(msg + 1, frame + 1, sizeoftlvs);
 | |
|         retval = msg_to_kernel(msg, sizeof(struct atmlec_msg) + sizeoftlvs);
 | |
|         
 | |
|         free(msg);
 | |
|         
 | |
|         return retval;
 | |
| }
 | |
| 
 | |
| /* Handles incoming LE_ARP_RESPONSE frames.
 | |
|  * Returns < 0 for serious error
 | |
|  */
 | |
| static int handle_arp_rsp(struct ctrl_frame *frame, int size)
 | |
| {
 | |
|         int sizeoftlvs, msglen, retval;
 | |
|         char buff[MAX_CTRL_FRAME];
 | |
|         struct atmlec_msg *msg;
 | |
|         
 | |
|         if (frame->header.lec_id != htons(lec_params.c14_lec_id)) {
 | |
|                 diag(COMPONENT, DIAG_DEBUG, "Wrong lec_id, ignoring\n");
 | |
|                 return 0;
 | |
|         }
 | |
| 
 | |
|         sizeoftlvs = size - sizeof(struct ctrl_frame);
 | |
|         msglen = sizeof(struct atmlec_msg) + sizeoftlvs;
 | |
|         msg = (struct atmlec_msg *)buff;
 | |
|         memset(msg, 0, msglen);
 | |
|         
 | |
|         msg->type = l_arp_update;
 | |
|         memcpy(msg->content.normal.mac_addr, frame->target_lan_dst.mac, ETH_ALEN);
 | |
|         memcpy(msg->content.normal.atm_addr, frame->target_atm_addr, ATM_ESA_LEN);
 | |
|         msg->content.normal.flag = (frame->header.flags & REMOTE_ADDRESS) ? 1 : 0;
 | |
|         msg->sizeoftlvs = sizeoftlvs;
 | |
|         if (sizeoftlvs > 0) memcpy(msg + 1, frame + 1, sizeoftlvs);
 | |
| 
 | |
|         retval = msg_to_kernel(msg, msglen);
 | |
| 
 | |
|         return retval;
 | |
| }
 | |
| 
 | |
| /* Handles incoming LE_TOPOLOGY_REQUEST frames.
 | |
|  * Returns < 0 for serious error
 | |
|  */
 | |
| static int handle_topo_req(struct ctrl_frame *frame)
 | |
| {
 | |
|         struct atmlec_msg msg;
 | |
| 
 | |
|         memset(&msg, 0, sizeof(struct atmlec_msg));
 | |
|         msg.type = l_topology_change;
 | |
|         if (frame->header.flags & htons(TOPO_CHANGE))
 | |
|                 msg.content.normal.flag = 1;
 | |
| 
 | |
|         return(msg_to_kernel(&msg, sizeof(struct atmlec_msg)));
 | |
| }
 | |
| 
 | |
| /* Processes and validates incoming frames. Calls frame
 | |
|  * dependant handler functions.
 | |
|  * Returns < 0 for serious error
 | |
|  */
 | |
| int handle_frame(Conn_t *conn, char *buff, int size)
 | |
| {
 | |
|         struct ctrl_frame *frame;
 | |
| 
 | |
|         if (validate_frame(buff, size) < 0)
 | |
|                 return 0;
 | |
| 
 | |
|         frame = (struct ctrl_frame *)buff;
 | |
| 
 | |
|         switch (ntohs(frame->header.opcode)) {
 | |
|         case LE_FLUSH_REQ:
 | |
|                 handle_flush_req(frame);
 | |
|                 break;
 | |
|         case LE_FLUSH_RSP:
 | |
|                 handle_flush_rsp(frame);
 | |
|                 break;
 | |
|         case READY_QUERY:
 | |
|                 handle_ready_query(conn, (struct ready_frame *)frame);
 | |
|                 break;
 | |
|         case READY_IND:
 | |
|                 /* We can ignore these */
 | |
|                 break;
 | |
|         case LE_ARP_REQ:
 | |
|                 if (handle_le_arp_req(frame, size) < 0)
 | |
|                         return -1;
 | |
|                 break;
 | |
|         case LE_ARP_RSP:
 | |
|                 if (handle_arp_rsp(frame, size) < 0)
 | |
|                         return -1;
 | |
|                 break;
 | |
|         case LE_TOPO_REQ:
 | |
|                 if (handle_topo_req(frame) < 0)
 | |
|                         return -1;
 | |
|                 break;
 | |
|         case LE_REG_RSP:
 | |
|                 /* FIXME: Should we do something? */
 | |
|                 break;
 | |
|         case LE_NARP_REQ:
 | |
|                 if (handle_narp_req(frame, size) < 0)
 | |
|                         return -1;
 | |
|                 break;
 | |
|         default:
 | |
|                 diag(COMPONENT, DIAG_ERROR,
 | |
|                      "Unknown frame, opcode 0x%x %s\n", ntohs(frame->header.opcode),
 | |
|                      opcode2text(frame->header.opcode));
 | |
|                 break;
 | |
|         }
 | |
| 
 | |
|         return 0;
 | |
| }
 | |
| 
 | |
| /* Sends a READY_INDICATION when a non-blocking connect completes.
 | |
|  */
 | |
| void send_ready_ind(Conn_t *conn)
 | |
| {
 | |
|         struct ready_frame frame;
 | |
|         int retval;
 | |
| 
 | |
|         frame.marker   = htons(0xff00);
 | |
|         frame.protocol = 0x01;
 | |
|         frame.version  = 0x01;
 | |
|         frame.opcode   = htons(READY_IND);
 | |
| 
 | |
|         retval = send_frame(conn, &frame, sizeof(struct ready_frame));
 | |
|         if (retval < 0)
 | |
|                 diag(COMPONENT, DIAG_DEBUG, "Could not send READY_IND, fd %d\n", conn->fd);
 | |
| 
 | |
|         return;
 | |
| }
 | |
| 
 | |
| /* Sends a LE_FLUSH_REQUEST
 | |
|  * Returns the transaction used with this flush REQ/RSP pair.
 | |
|  */
 | |
| uint32_t send_flush_req(Conn_t *conn)
 | |
| {
 | |
|         struct ctrl_frame frame;
 | |
| 
 | |
|         prefill_frame(&frame, LE_FLUSH_REQ);
 | |
|         memcpy(frame.src_atm_addr, lec_params.c1n_my_atm_addr, ATM_ESA_LEN);
 | |
|         memcpy(frame.target_atm_addr, conn->atm_address, ATM_ESA_LEN);
 | |
| 
 | |
|         send_frame(lec_params.mcast_send, &frame, sizeof(struct ctrl_frame));
 | |
| 
 | |
|         return ntohl(frame.header.tran_id);
 | |
| }
 | |
| 
 | |
| /* Registers our MAC address and associated TLVs with LES.
 | |
|  * See LANEv2, 6. Registaration Protocol
 | |
|  */
 | |
| void send_register_req(void)
 | |
| {
 | |
|         char buff[MAX_CTRL_FRAME];
 | |
|         struct ctrl_frame *frame;
 | |
| 
 | |
|         frame = (struct ctrl_frame *)buff;
 | |
|         prefill_frame(frame, LE_REG_REQ);
 | |
|         frame->src_lan_dst.tag = htons(LAN_DST_MAC_ADDR);
 | |
|         memcpy(frame->src_lan_dst.mac, lec_params.c6_mac_addr, ETH_ALEN);
 | |
|         memcpy(frame->src_atm_addr, lec_params.c1n_my_atm_addr, ATM_ESA_LEN);
 | |
|         frame->num_tlvs = lec_params.num_tlvs;
 | |
|         if (lec_params.sizeoftlvs > 0)
 | |
|                 memcpy((frame + 1), lec_params.tlvs, lec_params.sizeoftlvs);
 | |
| 
 | |
|         send_frame(lec_params.ctrl_direct, frame, sizeof(struct ctrl_frame) + lec_params.sizeoftlvs);
 | |
|         
 | |
|         return;
 | |
| }
 | |
| 
 | |
| /* Goes through the TLVs trailing a frame while passing them
 | |
|  * one by one to the TLV handler.
 | |
|  */
 | |
| void parse_tlvs(uint16_t opcode, unsigned char *tlvp, int numtlvs, int sizeoftlvs)
 | |
| {
 | |
|         uint32_t type;
 | |
|         uint8_t len;
 | |
|         unsigned char *end_of_tlvs;
 | |
| 
 | |
|         end_of_tlvs = tlvp + sizeoftlvs;
 | |
|         while (numtlvs-- > 0 && end_of_tlvs - tlvp >= 5) {
 | |
|                 type = *(uint32_t *)tlvp;
 | |
|                 type = ntohl(type);
 | |
|                 len  = tlvp[4];
 | |
|                 tlvp += 5;
 | |
|                 diag(COMPONENT, DIAG_DEBUG, "parse_tlvs: type %s len %d\n",
 | |
|                      tlv2text(type), len);
 | |
|                 if (tlvp + len > end_of_tlvs)
 | |
|                         return; /* value too long */
 | |
|                 
 | |
|                 extract_tlv_value(opcode, type, tlvp, len);
 | |
|                 tlvp += len;
 | |
|         }
 | |
| 
 | |
|         return;
 | |
| }
 | |
| 
 | |
| /* Does something depending on the frame type this TLV arrived with,
 | |
|  * TLV type, and contents of TLV.
 | |
|  */
 | |
| static void extract_tlv_value(uint16_t opcode, uint32_t type, unsigned char *tlvp, int len)
 | |
| {
 | |
| 
 | |
|         uint16_t value16;
 | |
|         uint32_t value32;
 | |
| 
 | |
|         /* LE_JOIN_RESPONSE does not support all the TLVs */
 | |
|         if (opcode == htons(LE_JOIN_RSP) && type != htonl(ELAN_ID))
 | |
|                 return;
 | |
| 
 | |
|         switch(len) {
 | |
|         case 0:
 | |
|                 switch (type) {
 | |
|                 case X5_ADJUSTMENT:
 | |
|                         handle_x5(opcode);
 | |
|                         break;
 | |
|                 default:
 | |
|                         goto whine;
 | |
|                         break;
 | |
|                 }
 | |
|                 break;
 | |
|         case 2:
 | |
|                 value16 = *(uint16_t *)tlvp;
 | |
|                 value16 = ntohs(value16);
 | |
|                 diag(COMPONENT, DIAG_DEBUG, "value of TLV %d\n", value16);
 | |
|                 switch (type) {
 | |
|                 case MAX_CUM_CTRL_TIMEOUT:
 | |
|                         lec_params.c7_ctrl_timeout = value16;
 | |
|                         break;
 | |
|                 case MAX_UNKNOWN_FRAME_CNT:
 | |
|                         lec_params.c10_max_unknown_frames = value16;
 | |
|                         break;
 | |
|                 case MAX_UNKNOWN_FRAME_TIME:
 | |
|                         lec_params.c11_max_unknown_frame_time = value16;
 | |
|                         break;
 | |
|                 case MAX_RETRY_COUNT:
 | |
|                         lec_params.c13_max_retry_count = value16;
 | |
|                         break;
 | |
|                 case FORWARD_DELAY_TIME:
 | |
|                         lec_params.c18_forward_delay_time = value16;
 | |
|                         break;
 | |
|                 case EXPECTED_LE_ARP_TIME:
 | |
|                         lec_params.c20_le_arp_response_time = value16;
 | |
|                         break;
 | |
|                 case FLUSH_TIMEOUT:
 | |
|                         lec_params.c21_flush_timeout = value16;
 | |
|                         break;
 | |
|                 case PATH_SWITCHING_DELAY:
 | |
|                         lec_params.c22_path_switching_delay = value16;
 | |
|                         break;
 | |
|                 case LOCAL_SEGMENT_ID:
 | |
|                 case DEF_MCAST_SND_VCC_TYPE:
 | |
|                 case CONN_COMPLETION_TIMER:
 | |
|                 case SERVICE_CATEGORY:
 | |
|                         /* do nothing */
 | |
|                         break;
 | |
|                 default:
 | |
|                         goto whine;
 | |
|                         break;
 | |
|                 }
 | |
|                 break;
 | |
|         case 4:
 | |
|                 value32 = *(uint32_t *)tlvp;
 | |
|                 value32 = ntohl(value32);
 | |
|                 diag(COMPONENT, DIAG_DEBUG, "value of TLV %d\n", value32);
 | |
|                 switch (type) {
 | |
|                 case VCC_TIMEOUT_PERIOD:
 | |
|                         lec_params.c12_vcc_timeout = value32;
 | |
|                         break;
 | |
|                 case AGING_TIME:
 | |
|                         lec_params.c17_aging_time = value32;
 | |
|                         break;
 | |
|                 case ELAN_ID:
 | |
|                         lec_params.c31_elan_id = value32;
 | |
|                         break;
 | |
|                 case DEF_MCAST_SND_VCC_AVG:
 | |
|                 case DEF_MCAST_SEND_PEAK_RT:
 | |
|                         /* do nothing */
 | |
|                         break;
 | |
|                 default:
 | |
|                         goto whine;
 | |
|                         break;
 | |
|                 }
 | |
|                 break;
 | |
|         case 20:
 | |
|                 switch(type) {
 | |
|                 case PREFERRED_LES:
 | |
|                         memcpy(lec_params.c35_preferred_les, tlvp, ATM_ESA_LEN);
 | |
|                         lec_params.c35_contains_address = 1;
 | |
|                         break;
 | |
|                 case LLC_MUXED_ATM_ADDR:
 | |
|                         /* do nothing */
 | |
|                         break;
 | |
|                 default:
 | |
|                         goto whine;
 | |
|                         break;
 | |
|                 }
 | |
|                 break;
 | |
|         default:
 | |
|                 /* handle variable length TLVs */
 | |
|                 switch(type) {
 | |
|                 case CONFIG_FRAG_INFO:
 | |
|                         diag(COMPONENT, DIAG_INFO, "Got Config-Frag-Info TLV\n");
 | |
|                         break;
 | |
|                 case LAYER3_ADDRESS:
 | |
|                         /* do nothing */
 | |
|                         break;
 | |
|                 default:
 | |
|                         goto whine;
 | |
|                         break;
 | |
|                 }
 | |
|                 break;
 | |
|         }
 | |
| 
 | |
|         return;
 | |
| 
 | |
|  whine:
 | |
|         diag(COMPONENT, DIAG_DEBUG, "Unknown TLV, type 0x%x, len %d\n", type, len);
 | |
| 
 | |
|         return;
 | |
| }
 | |
| 
 | |
| /* Figures out what to do when we get a X5-Adjustment TLV
 | |
|  */
 | |
| static void handle_x5(uint16_t opcode)
 | |
| {
 | |
|         if (!lec_params.c29_v2_capable)
 | |
|                 return;
 | |
|         if (opcode != ntohs(LE_CONFIG_RSP)) {
 | |
|                 diag(COMPONENT, DIAG_WARN, "X5-Adjustment TLV received but not with LE_CONFIG_RSP\n");
 | |
|                 return;
 | |
|         }
 | |
| 
 | |
|         switch(lec_params.c3_max_frame_size) {
 | |
|         case 1:
 | |
|                 lec_params.c3_max_frame_size = 5; /* 1580 */
 | |
|                 return;
 | |
|                 break;
 | |
|         case 2:
 | |
|                 lec_params.c3_max_frame_size = 5; /* 1580 */
 | |
|                 return;
 | |
|                 break;
 | |
|         default:
 | |
|                 /* rest of the values are not affected by X5 */
 | |
|                 break;
 | |
|         }
 | |
| 
 | |
|         return;
 | |
| }
 |