summaryrefslogtreecommitdiff
path: root/polux/application/ndd
diff options
context:
space:
mode:
Diffstat (limited to 'polux/application/ndd')
-rw-r--r--polux/application/ndd/Makefile43
-rw-r--r--polux/application/ndd/src/config_creator.c1185
-rw-r--r--polux/application/ndd/src/ndd.c488
3 files changed, 1716 insertions, 0 deletions
diff --git a/polux/application/ndd/Makefile b/polux/application/ndd/Makefile
new file mode 100644
index 0000000000..55493fe98f
--- /dev/null
+++ b/polux/application/ndd/Makefile
@@ -0,0 +1,43 @@
+TOPDIR=../..
+APP_DIR=$(TOPDIR)/application
+ifndef LINUX_DIR
+ LINUX_DIR=$(TOPDIR)/linux-2.6.10
+endif
+SRCDIR=src
+OBJDIR=obj
+SPIDLIB_DIR=$(APP_DIR)/spidlib
+SMMLIB_DIR=$(APP_DIR)/smm/smm-client
+TARGET=ndd
+SPIDLIB=$(SPIDLIB_DIR)/spidlib.a
+SMMLIB=$(SMMLIB_DIR)/smmlib.a
+OBJS=ndd.o config_creator.o
+OBJS:=$(addprefix $(OBJDIR)/,$(OBJS))
+VPATH=$(SRCDIR)
+INCLUDE=-I$(SPIDLIB_DIR)/inc -I$(SMMLIB_DIR)/inc -I$(TOPDIR)/include -I$(LINUX_DIR)/include -I$(TOPDIR)/include/plc-drv
+CFLAGS=-march=armv5 -msoft-float -Wall
+LDFLAGS=-march=armv5 -msoft-float -Wl -elf2flt='-s 8k'
+CC=arm-uclinux-gcc
+LD=arm-uclinux-gcc
+
+.PHONY: all clean distclean
+
+all: $(TARGET)
+
+$(TARGET): $(OBJDIR) $(OBJS) $(SPIDLIB) $(SMMLIB)
+ $(LD) -o $@ $(OBJS) $(SMMLIB) $(SPIDLIB) $(LDFLAGS)
+
+$(OBJDIR):
+ mkdir -p $(OBJDIR);
+
+$(OBJDIR)/%.o : %.c
+ $(CC) -MMD $(CFLAGS) $(INCLUDE) -c -o $@ $<
+
+-include $(OBJDIR)/*.d
+
+distclean: clean
+ rm -f $(SRCDIR)/*~ *~ *.gdb
+
+clean:
+ rm -rf $(TARGET) $(OBJDIR)/*
+
+
diff --git a/polux/application/ndd/src/config_creator.c b/polux/application/ndd/src/config_creator.c
new file mode 100644
index 0000000000..be3eb181a6
--- /dev/null
+++ b/polux/application/ndd/src/config_creator.c
@@ -0,0 +1,1185 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <syslog.h>
+#include <sys/stat.h>
+
+#include "spidlib.h"
+
+
+//***************************************************************************************************************
+/* PATH */
+#define TEMP_DIR "/tmp/todelete"
+#define TEMP_ETC_DIR "/tmp/todelete/etc"
+#define TEMP_RCSD_DIR "/tmp/todelete/etc/rcS.d"
+#define SLAVE_FILE TEMP_RCSD_DIR"/88slave"
+//#define NET_IFACE_FILE TEMP_DIR"/etc/network/interface"
+#define SYSCTL_CONF_FILE TEMP_ETC_DIR"/sysctl.conf"
+
+/* CONST */
+#define DEFAULT_BCAST_LIMIT 100
+#define DEFAULT_UNKNOWN_LIMIT 100
+#define BURST_RATIO 10
+#define STATIC_MODE "1"
+#define DHCP_MODE "2"
+#define NO_VLAN_TAG_ASKED 5555
+
+#define SLAVE_MODE_SYSCTL "net.plc.mode = slave\n"
+
+/* PROTOTYPES */
+extern int create_net_interface_file (void);
+extern int create_RF_output_level_all_file (char* mac, unsigned int* architecture);
+extern int create_mac_limitation_all_file (void);
+extern int create_QOS_prio_file (int* nb_ports, spidlib_service_type_t* service_type, int* slave_index, int* vid_tag);
+extern int create_DBA_PIR_file (int* nb_ports, spidlib_service_type_t* service_type, int* slave_index, int* vid_tag);
+extern int tag_vlan_one_port (spidlib_service_type_t* service_type, int* slave_index, int* vid_tag);
+extern int tag_vlan_several_ports(int* nb_ports, spidlib_service_type_t* service_type, int* slave_index, int* vid_tag) ;
+extern int get_slave_config(char* mac, spidlib_service_type_t* service_type, int* slave_index);
+extern int config_creator(char* mac_address, unsigned int * nb_eth_ports, unsigned int* architecture);
+extern int create_IGMP_snooping_file (char * mac);
+
+
+ //****************************************************************************************************************
+
+int remove_temp_dir()
+{
+ /* Remove temp dir */
+ FILE* temp;
+ char command[256];
+
+ sprintf(command, "rm -rf %s", TEMP_DIR);
+ if ((temp = spidlib_popen(command, "r")) == NULL)
+ {
+ syslog(LOG_WARNING, "config_creator: error deleting temp directory: %s", command);
+ return -1;
+ }
+ spidlib_pclose(temp);
+
+ return 0;
+}
+
+int create_temp_dir()
+{
+ /* Create temp dir */
+ mkdir (TEMP_DIR,0777);
+ mkdir (TEMP_ETC_DIR,0777);
+ mkdir (TEMP_RCSD_DIR,0777);
+ return 0;
+}
+
+int untar_config(char* path)
+{
+ /* Untar config in temp dir */
+ FILE* temp;
+ char command[256];
+
+ sprintf(command, "tar -vxf %s -C %s", path, TEMP_ETC_DIR);
+ if ((temp = spidlib_popen(command, "r")) == NULL)
+ {
+ syslog(LOG_WARNING, "config_creator: error untaring config: %s", command);
+ return -1;
+ }
+ spidlib_pclose(temp);
+
+ return 0;
+}
+
+int tar_config(char* mac)
+{
+ /* Tar config in autoconfig directory */
+ FILE* temp;
+ char command[256];
+ int i;
+
+ /* Switch ":" to "_" in mac addresses */
+ for (i = 0; i < strlen(mac); i++)
+ {
+ if (mac[i] == ':')
+ {
+ mac[i] = '_';
+ }
+ }
+
+ sprintf(command, "cd /tmp/todelete/etc; tar -vcf %s/%s.tar *", SPIDLIB_AUTOCONF_PATH, mac);
+
+ if ((temp = spidlib_popen(command, "r")) == NULL)
+ {
+ syslog(LOG_WARNING, "config_creator: error tarring config: %s", command);
+ return -1;
+ }
+ spidlib_pclose(temp);
+ return 0;
+}
+
+int create_broadcast_file(void)
+{
+ char value[16];
+ int fd_broad = -1;
+ char temp[256];
+ unsigned int broad_limit = DEFAULT_BCAST_LIMIT;
+
+ /* Parse broadcast restriction config file and create a file for the config */
+ if ((spidlib_read_config_param(SPIDLIB_CONFIG_SYSTEM_PATH, SPIDLIB_ADMIN_BROADCAST_LIMIT_EN_KEY, value, sizeof(value)) != 0)
+ || !SPIDLIB_GET_BOOLEAN (value))
+ {
+ return -1;
+ }
+
+ if (spidlib_read_config_param(SPIDLIB_CONFIG_SYSTEM_PATH, SPIDLIB_ADMIN_BROADCAST_LIMIT_KEY, value, sizeof(value)) >= 0)
+ {
+ broad_limit = strtoul(value, NULL, 10);
+ }
+
+ /* If value is null, avoid any broadcast limitations*/
+ if (broad_limit == 0 )
+ {
+ return 0;
+ }
+
+ if ((fd_broad = open(SLAVE_FILE, O_WRONLY| O_CREAT | O_APPEND, 0777)) < 0)
+ {
+ return -1;
+ }
+
+ write(fd_broad, "\n#Broadcast limit\n",18);
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp, "ebtables -t nat -A PREROUTING -i eth0 -d broadcast --limit %d/sec --limit-burst %d -j ACCEPT\n", broad_limit, broad_limit / BURST_RATIO);
+ write(fd_broad, temp, strlen(temp));
+
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp, "ebtables -t nat -A PREROUTING -i eth0 -d broadcast -j DROP\n");
+ write(fd_broad, temp, strlen(temp));
+
+ close(fd_broad);
+
+ return 0;
+}
+
+int create_unknown_file()
+{
+ char value[16];
+ int fd_unknown = -1;
+ char temp[256];
+ unsigned int unknown_limit = 0;
+
+ /* Parse broadcast restriction config file and create a file for the config */
+ if ((spidlib_read_config_param(SPIDLIB_CONFIG_SYSTEM_PATH, SPIDLIB_ADMIN_UNKNOWN_LIMIT_EN_KEY, value, sizeof(value)) != 0)
+ || !SPIDLIB_GET_BOOLEAN (value))
+ {
+ return -1;
+ }
+
+ if (spidlib_read_config_param(SPIDLIB_CONFIG_SYSTEM_PATH, SPIDLIB_ADMIN_UNKNOWN_LIMIT_KEY, value, sizeof(value)) >= 0)
+ {
+ unknown_limit = strtoul(value, NULL, 10);
+ }
+
+ /* If value is null, avoid any limitations for this parameter*/
+ if (unknown_limit == 0 ) return 0;
+
+ /* Parse unknown restriction config file and create a file for the config */
+ if ((fd_unknown = open(SLAVE_FILE, O_WRONLY| O_CREAT | O_APPEND, 0777)) < 0)
+ {
+ return -1;
+ }
+
+ write(fd_unknown, "\n#Unknown limit\n",16);
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp, "ebtables -t nat -A PREROUTING -i eth0 --plc-dst unknown --limit %d/sec --limit-burst %d -j ACCEPT\n", unknown_limit, unknown_limit / BURST_RATIO);
+ write(fd_unknown, temp, strlen(temp));
+
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp, "ebtables -t nat -A PREROUTING -i eth0 --plc-dst unknown -j DROP\n");
+ write(fd_unknown, temp, strlen(temp));
+
+ close(fd_unknown);
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ Function: create_net_interface_file
+ Description: Create a network interface file for slave configuration
+ Input: void
+ Output: 0 - execution is correct
+ -1 - there are errors
+ Error handling: Only return value should be checked, errno is not used.
+-----------------------------------------------------------------------------*/
+int create_net_interface_file (void)
+{
+#if 0 /* not used anymore */
+ char get_inet_mode[16];
+ char get_address[16];
+ char get_mask[16];
+ char get_gateway[16];
+ int fd_net_iface = -1;
+ char temp[256];
+
+
+ /* create /tmp/todelete/etc/network/interface directory*/
+ if ( mkdir("/tmp/todelete/etc/network",0777) !=0 )
+ {
+ syslog(LOG_WARNING, "config_creator: error creating /etc/network directory %d", errno);
+ return -1;
+ }
+
+ /*Parse whitelist_all_settings file to get wanted parameters*/
+ if (spidlib_read_config_param(SPIDLIB_WHITELIST_CONFIG_PATH, SPIDLIB_ALL_IP_MODE_KEY, get_inet_mode, sizeof(get_inet_mode)) != 0)
+ {
+ return -1;
+ }
+
+ /* ... If in "static" mode :*/
+ if (strncmp (get_inet_mode, STATIC_MODE, 1) == 0 )
+ {
+ /*Parse whitelist_all_settings file to get wanted parameters*/
+ if (spidlib_read_config_param(SPIDLIB_WHITELIST_CONFIG_PATH, SPIDLIB_ALL_IP_ADDRESS_KEY, get_address, sizeof(get_address)) != 0) return -1;
+ if (spidlib_read_config_param(SPIDLIB_WHITELIST_CONFIG_PATH, SPIDLIB_ALL_IP_MASK_KEY, get_mask, sizeof(get_mask)) != 0) return -1;
+ if (spidlib_read_config_param(SPIDLIB_WHITELIST_CONFIG_PATH, SPIDLIB_ALL_IP_GATEWAY_KEY, get_gateway, sizeof(get_gateway)) != 0) return -1;
+
+ /* Check validity of parameters*/
+ if ( (test_ip_addr(get_address) !=0)
+ || (test_ip_addr(get_mask) !=0)
+ || (test_ip_addr(get_gateway) !=0) )
+ {
+ return -1;
+ }
+
+ /* Fill in network interface file */
+ if ((fd_net_iface = open(NET_IFACE_FILE, O_WRONLY| O_CREAT | O_APPEND, 0777)) < 0) return -1;
+ write(fd_net_iface, "auto lo eth0 plc0 br0\n", 22);
+ write(fd_net_iface, "iface lo inet loopback\n", 23);
+ write(fd_net_iface, "iface eth0 inet static\n address 0.0.0.0\n netmask 255.255.255.0\n", 63);
+ write(fd_net_iface, "iface pcl0 inet static\n address 0.0.0.0\n netmask 255.255.255.0\n", 63);
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp, "\niface br0 inet static \n address %s\n netmask %s\n gateway %s \n", get_address, get_mask, get_gateway);
+ write(fd_net_iface, temp, strlen(temp));
+ }
+ else
+ {
+ /* ..... if "DHCP" mode*/
+ if (strncmp(get_inet_mode,DHCP_MODE, 1) == 0)
+ {
+ /* Fill in network interface file */
+ if ((fd_net_iface = open(NET_IFACE_FILE, O_WRONLY| O_CREAT | O_APPEND, 0777)) < 0) return -1;
+ write(fd_net_iface, "auto lo eth0 plc0 br0\n", 22);
+ write(fd_net_iface, "iface lo inet loopback\n", 23);
+ write(fd_net_iface, "iface eth0 inet static\n address 0.0.0.0\n netmask 255.255.255.0\n", 63);
+ write(fd_net_iface, "iface pcl0 inet static\n address 0.0.0.0\n netmask 255.255.255.0\n", 63);
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp, "\niface br0 inet dhcp\n");
+ write(fd_net_iface, temp, strlen(temp));
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ close(fd_net_iface);
+#endif
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ Function: create_RF_output_level_all_file
+ Description: Fill in net/plc/default_gain sysctl and
+ ce/data gain for all slaves
+ Input: void
+ Output: 0 - execution is correct
+ -1 - there are errors
+ Error handling: Only return value should be checked, errno is not used.
+-----------------------------------------------------------------------------*/
+int create_RF_output_level_all_file (char* mac, unsigned int* architecture)
+{
+ char get_RF_output_level_all[16];
+ unsigned int rf_level, i;
+ int fd_sysctl_conf = -1;
+ char temp[256];
+ spidlib_whitelist_entry_t wl_entry;
+
+ /* Check if common RF Output Level has to be set or specific RF Output Level */
+ /* If specific level is null, set common level; else, set the specific */
+ if (spidlib_get_whitelist_entry ( mac, &wl_entry ) < 0 )
+ {
+ return -1;
+ }
+ if (wl_entry.RFOutput_level != 0)
+ {
+ rf_level = wl_entry.RFOutput_level;
+ }
+ else
+ {
+ /*Parse whitelist_all_settings file to get wanted common parameter*/
+ if (spidlib_read_config_param(SPIDLIB_CONFIG_SYSTEM_PATH, SPIDLIB_RF_OUTPUT_LEVEL_KEY, get_RF_output_level_all, sizeof(get_RF_output_level_all)) != 0 )
+ {
+ return -1;
+ }
+
+ sscanf(get_RF_output_level_all, "%u", &rf_level);
+ }
+
+ /* Fill in sysctl.conf file */
+ if ((fd_sysctl_conf = open(SYSCTL_CONF_FILE, O_WRONLY| O_CREAT | O_APPEND, 0777)) < 0)
+ {
+ return -1;
+ }
+
+ /* Added slave mode to sysctl file */
+ write(fd_sysctl_conf, SLAVE_MODE_SYSCTL, sizeof(SLAVE_MODE_SYSCTL));
+
+
+ for (i = 0; i < OUTPUT_LEVEL_NB_LEVEL_VALUES ; i++)
+ {
+ if ( rf_level == output_level_tab[i][0] )
+ {
+ /* Fill in tx_gain, data_gain and default gain sysctl, depends on rf level value*/
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp, "net.plc.tx_gain=%d\nnet.plc.ce.data_gain=%d\nnet.plc.default_gain=%d\n", output_level_tab[i][1], output_level_tab[i][2], output_level_tab[i][2] );
+ write(fd_sysctl_conf, temp, strlen(temp));
+ close(fd_sysctl_conf);
+ return 0;
+ }
+ }
+ /*If wanted value is not in tab, send a syslog message and exit */
+ syslog(LOG_DEBUG, "config_creator: invalid output level value: %u %s", rf_level, get_RF_output_level_all);
+ close(fd_sysctl_conf);
+ return -1;
+}
+
+/*-----------------------------------------------------------------------------
+ Function: create_mac_limitation_all_file
+ Description: Fill in net/plc/default_gain sysctl and
+ ce/data gain for all slaves
+ Input: void
+ Output: 0 - execution is correct
+ -1 - there are errors
+ Error handling: Only return value should be checked, errno is not used.
+-----------------------------------------------------------------------------*/
+int create_mac_limitation_all_file (void)
+{
+ char get_mac_limitation[8];
+ int fd_sysctl_conf = -1;
+ char temp[256];
+ unsigned int mac_limt;
+
+ /*Parse whitelist_all_settings file to get wanted parameters*/
+ if (spidlib_read_config_param(SPIDLIB_CONFIG_SYSTEM_PATH, SPIDLIB_EXT_MAC_LIMITATION_KEY, get_mac_limitation, sizeof(get_mac_limitation)) != 0 )
+ {
+ return -1;
+ }
+
+ mac_limt = strtoul(get_mac_limitation, NULL, 10);
+ /* If value is null, avoid any mac limitations*/
+ if (mac_limt == 0 ) return 0;
+
+ /* Fill in sysctl.conf file */
+ if ((fd_sysctl_conf = open(SYSCTL_CONF_FILE, O_WRONLY| O_CREAT | O_APPEND, 0777)) < 0)
+ {
+ return -1;
+ }
+
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp, "net.plc.carp.mac_limit = %s \n", get_mac_limitation);
+ write(fd_sysctl_conf, temp, strlen(temp));
+
+ close(fd_sysctl_conf);
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ Function: create_QOS_prio_file
+ Description: Fill in net/plc/default_gain sysctl and
+ ce/data gain for all slaves
+ Input: void
+ Output: 0 - execution is correct
+ -1 - there are errors
+ Error handling: Only return value should be checked, errno is not used.
+-----------------------------------------------------------------------------*/
+int create_QOS_prio_file (int* nb_ports, spidlib_service_type_t* service_type, int* slave_index, int* vid_tag)
+{
+ spidlib_port_list_entry_t port_list_entry;
+ spidlib_service_list_entry_t service_list_entry;
+ int fd_slave = -1;
+ char temp_common[256];
+ int common_is_written = SPIDLIB_FALSE;
+ char temp[256];
+ int i;
+
+ // Check input parameters
+ if ((NULL == nb_ports)
+ || (NULL == service_type)
+ || (NULL == slave_index)) return -1;
+
+ /* In case of service type = NONE, don't affiliate any QOS prio*/
+ if ((*service_type) == SPIDLIB_SERVICE_TYPE_NONE)
+ return 0;
+
+ /* Fill in slave file */
+ if ((fd_slave = open (SLAVE_FILE, O_WRONLY| O_CREAT | O_APPEND, 0777)) < 0)
+ {
+ return -1;
+ }
+
+ /* Write common rules for all ports */
+ /* Write common rules in file only if at least one service needs it */
+ /* Don't write it here but in services treatements; see below*/
+ memset(temp_common, 0, sizeof(temp_common));
+ if (*nb_ports ==1)
+ {
+ if (*vid_tag == NO_VLAN_TAG_ASKED) sprintf(temp_common, "ebtables -N QOS \nebtables -A FORWARD -o plc0 -j QOS\n");
+ else {sprintf(temp_common, "ebtables -N QOS \nebtables -A FORWARD -o plc0.%d -j QOS\n", *vid_tag);}
+ }
+ else
+ {
+ sprintf(temp_common, "ebtables -N QOS \nebtables -A FORWARD -o plc0 -p 0x8100 -j QOS\n");
+ }
+
+
+ /* Get service, vlanTag and vlan mark_id matching with slave index*/
+ /* Do that for all Port_index field */
+ port_list_entry.portSlaveIndex = *slave_index ;
+
+ for (i = 1 ; i <= (*nb_ports); i++)
+ {
+ /* Get portTable entry in order to get service index*/
+ port_list_entry.portIndex = i;
+
+ if (spidlib_get_port_list_entry(&port_list_entry) < 0)
+ {
+ continue;
+ }
+
+ /* Treat this port only if enable */
+ if ((SPIDLIB_TRUE == port_list_entry.portEnable)
+ && (port_list_entry.serviceIndex > 0))
+ {
+ /* By service index, get suitable ServiceTable entry */
+ /* to obtain Qos Prio */
+ service_list_entry.index = port_list_entry.serviceIndex;
+ if (spidlib_get_service_list_entry(&service_list_entry) <0)
+ {
+ continue;
+ }
+
+ if(0 == service_list_entry.QOSprio)
+ {
+ service_list_entry.QOSprio++;
+ }
+ /* Build rules specific of each port (identify by service_inedx field) :
+ depends on type of service*/
+ switch (*service_type)
+ {
+ case SPIDLIB_SERVICE_TYPE_VID :
+ memset(temp, 0, sizeof(temp));
+ if (*nb_ports == 1)
+ {
+ if (*vid_tag == NO_VLAN_TAG_ASKED) sprintf(temp,"ebtables -A QOS -o plc0 -j mark --set-mark %d --mark-target RETURN\n", service_list_entry.QOSprio);
+ else { sprintf(temp,"ebtables -A QOS -o plc0.%d -j mark --set-mark %d --mark-target RETURN\n", *vid_tag, service_list_entry.QOSprio);}
+ }
+ else
+ {
+ sprintf(temp,"ebtables -A QOS -o plc0 -p 0x8100 --vlan-id %d -j mark --set-mark %d --mark-target RETURN\n", service_list_entry.matching_value, service_list_entry.QOSprio);
+ }
+
+ /* Write common rules only once, check if not still written*/
+ if (SPIDLIB_FALSE == common_is_written)
+ {
+ write(fd_slave, "\n#Set QoS prio\n",15);
+ write(fd_slave, temp_common, strlen(temp_common));
+ common_is_written = SPIDLIB_TRUE;
+ }
+ /* Write specific rules*/
+ write(fd_slave, temp, strlen(temp));
+ break;
+
+ case SPIDLIB_SERVICE_TYPE_PRIO :
+ memset(temp, 0, sizeof(temp));
+ if (*nb_ports == 1)
+ {
+ if (*vid_tag == NO_VLAN_TAG_ASKED) sprintf(temp,"ebtables -A QOS -o plc0 -j mark --set-mark %d --mark-target RETURN\n", service_list_entry.QOSprio);
+ else { sprintf(temp,"ebtables -A QOS -o plc0.%d -j mark --set-mark %d --mark-target RETURN\n", *vid_tag, service_list_entry.QOSprio);}
+ }
+ else
+ {
+ sprintf(temp,"ebtables -A QOS -o plc0 -p 0x8100 --vlan-prio %d -j mark --set-mark %d --mark-target RETURN\n", service_list_entry.matching_value, service_list_entry.QOSprio);
+ }
+ /* Write common rules only once, check if not still written*/
+ if (SPIDLIB_FALSE == common_is_written)
+ {
+ write(fd_slave, "\n#Set QoS prio\n",15);
+ write(fd_slave, temp_common, strlen(temp_common));
+ common_is_written = SPIDLIB_TRUE;
+ }
+ write(fd_slave, temp, strlen(temp));
+ break;
+
+ case SPIDLIB_SERVICE_TYPE_TOS :
+ memset(temp, 0, sizeof(temp));
+ if (*nb_ports == 1)
+ {
+ if (*vid_tag == NO_VLAN_TAG_ASKED) sprintf(temp,"ebtables -A QOS -o plc0 -j mark --set-mark %d --mark-target RETURN\n", service_list_entry.QOSprio);
+ else {sprintf(temp,"ebtables -A QOS -o plc0.%d -p 0x0800 --ip-tos 0x%02x -j mark --set-mark %d --mark-target RETURN\n", *vid_tag, service_list_entry.matching_value, service_list_entry.QOSprio);}
+ }
+ else
+ {
+ sprintf(temp,"ebtables -A QOS -o plc0 -p 0x8100 --vlan-encap 0x0800 --vlan-tos 0x%02x -j mark --set-mark %d --mark-target RETURN\n", service_list_entry.matching_value, service_list_entry.QOSprio);
+ }
+ /* Write common rules only once, check if not still written*/
+ if (SPIDLIB_FALSE == common_is_written)
+ {
+ write(fd_slave, "\n#Set QoS prio\n",15);
+ write(fd_slave, temp_common, strlen(temp_common));
+ common_is_written = SPIDLIB_TRUE;
+ }
+ write(fd_slave, temp, strlen(temp));
+ break;
+
+ default :
+ close(fd_slave);
+ return -1;
+ }
+ }
+ }
+ close(fd_slave);
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ Function: create_DBA_PIR_file
+ Description:
+ Input: number of ports for this slave
+ type of service (vprio, vid or tos)
+ index of slave
+ Output: 0 - execution is correct
+ -1 - there are errors
+ Error handling: Only return value should be checked, errno is not used.
+-----------------------------------------------------------------------------*/
+int create_DBA_PIR_file (int* nb_ports, spidlib_service_type_t* service_type, int* slave_index, int* vid_tag)
+{
+ spidlib_port_list_entry_t port_list_entry;
+ spidlib_service_list_entry_t service_list_entry;
+ int fd_slave = -1;
+ char temp[256];
+ char temp_common_eth[256];
+ char temp_common_plc[256];
+ int i;
+ unsigned int burst_down;
+ unsigned int burst_up;
+ int common_is_written = SPIDLIB_FALSE;
+
+ // Check input parameters
+ if ((NULL == nb_ports)
+ || (NULL == service_type)
+ || (NULL == slave_index)) return -1;
+
+
+ /* Open a File Descriptor for writting rules*/
+ if ((fd_slave = open (SLAVE_FILE, O_WRONLY| O_CREAT | O_APPEND, 0777)) < 0)
+ {
+ return -1;
+ }
+
+ /* Write common rules for any type of service */
+ /* Write common rules in file only if at least one service needs it */
+ /* Don't write it here but in services treatements; see below*/
+ /*... For plc0*/
+ memset(temp_common_plc, 0, sizeof(temp_common_plc));
+ sprintf(temp_common_plc,"tc qdisc add dev plc0 root handle 1: htb default 99\ntc class add dev plc0 parent 1: classid 1:1 htb rate 70mbit\n");
+
+ /*...and for eth0*/
+ memset(temp_common_eth, 0, sizeof(temp_common_eth));
+ sprintf(temp_common_eth,"tc qdisc add dev eth0 root handle 1: htb default 99\ntc class add dev eth0 parent 1: classid 1:1 htb rate 70mbit\n");
+
+
+ /* Get service, vlanTag and vlan mark_id matching with slave index*/
+ /* Do that for all Port_index field */
+ port_list_entry.portSlaveIndex = *slave_index ;
+
+ for (i = 1 ; i <= (*nb_ports); i++)
+ {
+ /* Get portTable entry in order to get service index*/
+ port_list_entry.portIndex = i;
+ if (spidlib_get_port_list_entry(&port_list_entry) < 0)
+ {
+ continue;
+ }
+
+ /* Treat this port only if enable */
+ if ((SPIDLIB_TRUE == port_list_entry.portEnable)
+ && (port_list_entry.serviceIndex > 0))
+ {
+ /* By service index, get suitable ServiceTable entry */
+ /* to obtain DBA PIR limitations*/
+ service_list_entry.index = port_list_entry.serviceIndex;
+ if (spidlib_get_service_list_entry(&service_list_entry) <0)
+ {
+ continue;
+ }
+
+ /* Apply limitations only if DBA_PIR values are != 0 */
+ if ( (0 != service_list_entry.DBADownlinkPIR) || (0 !=service_list_entry.DBAUplinkPIR))
+ {
+ // Determination of "burst" parameter depends on rate (= DBA_PIR) value:
+ // Minimal burst is calculated like that : burst = rate / HZ / 8 ; (with HZ =100 ms, here)
+ // but to have more margin, we apply this formula below :
+ burst_down = ((service_list_entry.DBADownlinkPIR)*2) / (HZ/8);
+ burst_up = ((service_list_entry.DBAUplinkPIR)*2) / (HZ/8);
+
+ if (0 == service_list_entry.QOSprio)
+ {
+ service_list_entry.QOSprio++;
+ }
+ /* Define a class for each service_index and define a queue per class*/
+ /*...For Downlink */
+ if (0 != service_list_entry.DBADownlinkPIR)
+ {
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp,"tc class add dev eth0 parent 1:1 classid 1:%d htb rate %dkbit burst %dk \n", service_list_entry.index + 10, service_list_entry.DBADownlinkPIR, burst_down);
+ /* Write common rules only once, check if not still written*/
+ if (SPIDLIB_FALSE == common_is_written)
+ {
+ write(fd_slave, "\n#Set DBA PIR\n",14);
+ write(fd_slave, temp_common_plc, strlen(temp_common_plc));
+ write(fd_slave, temp_common_eth, strlen(temp_common_eth));
+ common_is_written = SPIDLIB_TRUE;
+ }
+ write(fd_slave, temp, strlen(temp));
+
+ // seems not necessary
+ /*memset(temp, 0, sizeof(temp));
+ sprintf(temp,"tc qdisc add dev plc0 parent 1:%d handle %d: pfifo\n", service_list_entry.index + 10, service_list_entry.QOSprio);
+ write(fd_slave, temp, strlen(temp));*/
+ }
+
+ /*For Uplink*/
+ if (0 != service_list_entry.DBAUplinkPIR)
+ {
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp,"tc class add dev plc0 parent 1:1 classid 1:%d htb rate %dkbit burst %dk \n", service_list_entry.index + 10, service_list_entry.DBAUplinkPIR, burst_up);
+
+ /* Write common rules only once, check if not still written*/
+ if (SPIDLIB_FALSE == common_is_written)
+ {
+ write(fd_slave, "\n#Set DBA PIR\n",14);
+ write(fd_slave, temp_common_plc, strlen(temp_common_plc));
+ write(fd_slave, temp_common_eth, strlen(temp_common_eth));
+ common_is_written = SPIDLIB_TRUE;
+ }
+ write(fd_slave, temp, strlen(temp));
+
+ // seems not necessary
+ /*memset(temp, 0, sizeof(temp));
+ sprintf(temp,"tc qdisc add dev eth0 parent 1:%d handle %d: pfifo\n", service_list_entry.index + 10, service_list_entry.QOSprio);
+ write(fd_slave, temp, strlen(temp));*/
+ }
+
+ /* Write rules to limit traffic */
+ /* depending on type of service*/
+ switch (*service_type)
+ {
+ case SPIDLIB_SERVICE_TYPE_VID :
+ memset(temp, 0, sizeof(temp));
+ if (*nb_ports == 1)
+ {
+ if (*vid_tag == NO_VLAN_TAG_ASKED) sprintf(temp,"ebtables -A FORWARD -p 0x8100 --vlan-id %d -j mark --set-mark %d --mark-target CONTINUE\n", service_list_entry.matching_value, service_list_entry.QOSprio);
+ else{ sprintf(temp,"ebtables -A FORWARD -j mark --set-mark %d --mark-target CONTINUE\n", service_list_entry.QOSprio); }
+ }
+ else
+ {
+ sprintf(temp,"ebtables -A FORWARD -i plc0 -p 0x8100 --vlan-id %d -j mark --set-mark %d --mark-target CONTINUE\n", service_list_entry.matching_value, service_list_entry.QOSprio);
+ }
+
+ write(fd_slave, temp, strlen(temp));
+ break;
+
+ case SPIDLIB_SERVICE_TYPE_PRIO:
+ memset(temp, 0, sizeof(temp));
+ if (*nb_ports == 1)
+ {
+ if (*vid_tag == NO_VLAN_TAG_ASKED) sprintf(temp,"ebtables -A FORWARD -p 0x8100 --vlan-prio %d -j mark --set-mark %d --mark-target CONTINUE\n", service_list_entry.matching_value, service_list_entry.QOSprio);
+ else
+ {
+ /* Limit DBA PIR for upload (slave->master) */
+ sprintf(temp,"ebtables -A FORWARD -o plc0.%d -j mark --set-mark %d --mark-target CONTINUE\n", *vid_tag, service_list_entry.QOSprio);
+
+ /* Limit for download : NA*/
+ syslog(LOG_WARNING, "config_creator: cannot limit DBA_PIR in Download in case of service_type = VPRIO and VlanTag=enable : Non Applicable");
+ }
+ }
+ else
+ {
+ sprintf(temp,"ebtables -A FORWARD -i plc0 -p 0x8100 --vlan-prio %d -j mark --set-mark %d --mark-target CONTINUE\n", service_list_entry.matching_value, service_list_entry.QOSprio);
+ }
+
+ write(fd_slave, temp, strlen(temp));
+ break;
+
+ case SPIDLIB_SERVICE_TYPE_TOS:
+ memset(temp, 0, sizeof(temp));
+ if (*nb_ports == 1)
+ {
+ if (*vid_tag == NO_VLAN_TAG_ASKED) sprintf(temp,"ebtables -A FORWARD -p 0x8100 --vlan-encap 0x0800 --vlan-tos 0x%02x -j mark --set-mark %d --mark-target CONTINUE\nebtables -A FORWARD -p 0x0800 --ip-tos 0x%02x -j mark --set-mark %d --mark-target CONTINUE\n", service_list_entry.matching_value, service_list_entry.QOSprio, service_list_entry.matching_value, service_list_entry.QOSprio);
+ else {sprintf(temp,"ebtables -A FORWARD -p 0x0800 --ip-tos 0x%02x -j mark --set-mark %d --mark-target CONTINUE\n", service_list_entry.matching_value, service_list_entry.QOSprio);}
+ }
+ else
+ {
+ sprintf(temp,"ebtables -A FORWARD -i plc0 -p 0x8100 --vlan-encap 0x0800 --vlan-tos 0x%02x -j mark --set-mark %d --mark-target CONTINUE\nebtables -A FORWARD -p 0x0800 --ip-tos 0x%02x -j mark --set-mark %d --mark-target CONTINUE\n", service_list_entry.matching_value, service_list_entry.QOSprio, service_list_entry.matching_value, service_list_entry.QOSprio);
+ }
+
+ write(fd_slave, temp, strlen(temp));
+ break;
+
+ case SPIDLIB_SERVICE_TYPE_NONE :
+ memset(temp, 0, sizeof(temp));
+ /* Set this limitation to an unlikely mark value (here "88") */
+ /* in order to avoid to match with possible QOS values */
+ sprintf(temp,"ebtables -A FORWARD -j mark --set-mark 88 --mark-target CONTINUE\n");
+ write(fd_slave, temp, strlen(temp));
+
+ /*For Downlink ...*/
+ if (0 != service_list_entry.DBADownlinkPIR)
+ {
+ memset(temp, 0, sizeof(temp));
+ /* Set this limitation to an unlikely mark value (here "88") */
+ /* in order to avoid to match with possible QOS values */
+ sprintf(temp,"tc filter add dev eth0 parent 1: protocol all handle 88 fw flowid 1:%d\n", service_list_entry.index + 10);
+ write(fd_slave, temp, strlen(temp));
+ }
+ /*... For Uplink*/
+ if (0 != service_list_entry.DBAUplinkPIR)
+ {
+ memset(temp, 0, sizeof(temp));
+ /* Set this limitation to an unlikely mark value (here "88") */
+ /* in order to avoid to match with possible QOS values */
+ sprintf(temp,"tc filter add dev plc0 parent 1: protocol all handle 88 fw flowid 1:%d\n", service_list_entry.index + 10);
+ write(fd_slave, temp, strlen(temp));
+ }
+ break;
+
+ default :
+ close(fd_slave);
+ return -1;
+ }
+
+ /* Limitations are still linked with tc filter rules for
+ service_type = NONE, so skip next lines */
+ if ( SPIDLIB_SERVICE_TYPE_NONE != (*service_type) )
+ {
+ /*... For Downlink*/
+ if (0 != service_list_entry.DBADownlinkPIR)
+ {
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp,"tc filter add dev eth0 parent 1: protocol all handle %d fw flowid 1:%d\n", service_list_entry.QOSprio, service_list_entry.index + 10);
+ write(fd_slave, temp, strlen(temp));
+ }
+ /*... For Uplink*/
+ if (0 != service_list_entry.DBAUplinkPIR)
+ {
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp,"tc filter add dev plc0 parent 1: protocol all handle %d fw flowid 1:%d\n", service_list_entry.QOSprio, service_list_entry.index + 10);
+ write(fd_slave, temp, strlen(temp));
+ }
+ }
+ }
+ }
+ }
+ close(fd_slave);
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ Function: tag_vlan_one_port
+ Description: rules for tagging vlan from eth to plc
+ Input: type of service (vprio, vid or tos)
+ index of slave
+ Output: 0 - execution is correct
+ -1 - there are errors
+ Error handling: Only return value should be checked, errno is not used.
+-----------------------------------------------------------------------------*/
+int tag_vlan_one_port (spidlib_service_type_t* service_type, int* slave_index, int* vid_tag)
+{
+ spidlib_port_list_entry_t port_list_entry;
+ spidlib_service_list_entry_t service_list_entry;
+ int fd_slave = -1;
+ char temp[256];
+
+ // Check input parameters
+ if ((NULL == service_type)
+ || (NULL == slave_index)) return -1;
+
+ // In case of service type = NONE, don't tag any port with vlanTag
+ if ((*service_type) == SPIDLIB_SERVICE_TYPE_NONE)
+ return 0;
+
+ /* Get service, vlanTag and vlan mark_id matching with slave index*/
+ /* Port_index field = 1 because this is the case of one port */
+ port_list_entry.portSlaveIndex = *slave_index ;
+ port_list_entry.portIndex = 1;
+ if (spidlib_get_port_list_entry(&port_list_entry) <0)
+ return -1;
+
+ /* Treat this port only if enable */
+ if ((SPIDLIB_TRUE == port_list_entry.portEnable)
+ && (SPIDLIB_TRUE == port_list_entry.VlanTag))
+ {
+ /* Open a File Descriptor for writting rules*/
+ if ((fd_slave = open (SLAVE_FILE, O_WRONLY| O_CREAT | O_APPEND, 0777)) < 0)
+ {
+ return -1;
+ }
+
+ /* Write rules according to type of services*/
+ write(fd_slave, "\n#Tag Vlans for one port\n",25);
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp,"vconfig add plc0 %d\n"
+ "brctl delif br0 plc0\n"
+ "brctl addif br0 plc0.%d\n"
+ "ifconfig plc0.%d 0.0.0.0\n", port_list_entry.vlanMarkId, port_list_entry.vlanMarkId, port_list_entry.vlanMarkId);
+ write(fd_slave, temp, strlen(temp));
+ *vid_tag = port_list_entry.vlanMarkId;
+
+ service_list_entry.index = port_list_entry.serviceIndex;
+ if ((port_list_entry.serviceIndex > 0)
+ && (spidlib_get_service_list_entry(&service_list_entry) >= 0))
+ {
+ if (SPIDLIB_SERVICE_TYPE_PRIO == *service_type)
+ {
+ /* Set vlan prio*/
+ /* vconfig set_egress_map plc0.<vid> <skb_prio> <vlan_qos>*/
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp,"vconfig set_egress_map plc0.%d 0 %d \n", port_list_entry.vlanMarkId, service_list_entry.matching_value);
+ write(fd_slave, temp, strlen(temp));
+ }
+ }
+ close(fd_slave);
+ }
+ else { syslog(LOG_NOTICE, "config_creator: no vlan rule created (port or vlan tag disabled)"); }
+
+ return 0;
+}
+
+
+/*-----------------------------------------------------------------------------
+ Function: tag_vlan_several_ports
+ Description: rules for tagging vlan from eth to plc
+ in case of several ports on slave
+ Input: number of ports for this slave
+ type of service (vprio, vid or tos)
+ index of slave
+ Output: 0 - execution is correct
+ -1 - there are errors
+ Error handling: Only return value should be checked, errno is not used.
+-----------------------------------------------------------------------------*/
+int tag_vlan_several_ports(int* nb_ports, spidlib_service_type_t* service_type, int* slave_index, int* vid_tag)
+{
+ spidlib_port_list_entry_t port_list_entry;
+ spidlib_service_list_entry_t service_list_entry;
+ int fd_slave = -1;
+ char temp[256];
+ int i;
+
+ // Check input parameters
+ if ((NULL == nb_ports)
+ || (NULL == service_type)
+ || (NULL == slave_index)) return -1;
+
+ // In case of service type is NONE, don't tag any port with vlan
+ if ((*service_type) == SPIDLIB_SERVICE_TYPE_NONE)
+ return 0;
+
+ /* Open a File Descriptor for writting rules*/
+ if ((fd_slave = open (SLAVE_FILE, O_WRONLY| O_CREAT | O_APPEND, 0777)) < 0)
+ {
+ return -1;
+ }
+
+ /* Get service, vlanTag and vlan mark_id matching with slave index*/
+ /* Do that for all Port_index field */
+ port_list_entry.portSlaveIndex = *slave_index ;
+
+ write(fd_slave, "\n#Tag Vlans for several ports\n",30);
+
+ for (i = 1 ; i <= (*nb_ports); i++)
+ {
+ port_list_entry.portIndex = i;
+ if (spidlib_get_port_list_entry(&port_list_entry) < 0)
+ {
+ continue;
+ }
+
+ /* Treat this port only if enable */
+ if ((SPIDLIB_TRUE == port_list_entry.portEnable)
+ && (SPIDLIB_TRUE == port_list_entry.VlanTag))
+ {
+ /*Write rules to tag vlans*/
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp,"ethtool -vl eth0 set-phy-vid %d:%d set-phy-state %d:on\n", port_list_entry.portIndex - 1, port_list_entry.vlanMarkId, port_list_entry.portIndex - 1);
+ write(fd_slave, temp, strlen(temp));
+ *vid_tag = port_list_entry.vlanMarkId;
+ }
+ service_list_entry.index = port_list_entry.serviceIndex;
+ if ((port_list_entry.serviceIndex > 0)
+ && (spidlib_get_service_list_entry(&service_list_entry) >= 0))
+ {
+ if (SPIDLIB_SERVICE_TYPE_PRIO == *service_type)
+ {
+ /* Set vlan prio*/
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp,"ethtool -vl eth0 set-phy-prio %d:%d set-phy-state %d:on\n", port_list_entry.portIndex - 1, service_list_entry.matching_value, port_list_entry.portIndex - 1);
+ write(fd_slave, temp, strlen(temp));
+ }
+ }
+ }
+ close(fd_slave);
+
+ return 0;
+}
+
+int find_config(char* mac, char* path)
+{
+ /* Finds path to the config for the provided mac if found */
+ int fd = 0, i = 0;
+ char path_real[256];
+ char mac_modified_lower[18];
+ char mac_modified_upper[18];
+ unsigned char bin[6];
+
+ if (spidlib_mac_str_to_bin(mac, bin) != 0)
+ {
+ syslog(LOG_WARNING, "config_creator: error switching mac to bin format");
+ return -1;
+ }
+
+ /* Get mac address in lower case */
+ if (spidlib_mac_bin_to_str_lower(bin, mac_modified_lower) != 0)
+ {
+ syslog(LOG_WARNING, "config_creator: error switching mac to lower string format");
+ return -1;
+ }
+
+ /* Get mac address in upper case */
+ if (spidlib_mac_bin_to_str_upper(bin, mac_modified_upper) != 0)
+ {
+ syslog(LOG_WARNING, "config_creator: error switching mac to upper string format");
+ return -1;
+ }
+
+ /* Switch ":" to "_" in mac addresses */
+ for (i = 0; i < strlen(mac_modified_lower); i++)
+ if (mac_modified_lower[i] == ':')
+ mac_modified_lower[i] = '_';
+
+ for (i = 0; i < strlen(mac_modified_upper); i++)
+ if (mac_modified_upper[i] == ':')
+ mac_modified_upper[i] = '_';
+
+ /* Previous config found (lower case) */
+ sprintf(path_real, "%s/%s.tar", SPIDLIB_USER_AUTOCONF_PATH, mac_modified_lower);
+ if ((fd = open(path_real, O_RDONLY)) > 2)
+ {
+ close(fd);
+ strcpy(path, path_real);
+ return 0;
+ }
+
+ /* Previous config found (upper case) */
+ sprintf(path_real, "%s/%s.tar", SPIDLIB_USER_AUTOCONF_PATH, mac_modified_upper);
+ if ((fd = open(path_real, O_RDONLY)) > 2)
+ {
+ close(fd);
+ strcpy(path, path_real);
+ return 0;
+ }
+
+ /* Default config found */
+ if ((fd = open(SPIDLIB_USER_DEFAULT_CONFIG_PATH, O_RDONLY)) > 2)
+ {
+ close(fd);
+ strcpy(path, SPIDLIB_USER_DEFAULT_CONFIG_PATH);
+ return 0;
+ }
+
+ return -1;
+}
+
+/*-----------------------------------------------------------------------------
+ Function: create_IGMP_snooping_file
+ Description: Create rules to prevent all multicast IGMP packets
+ for not allowed slaves in igmp whitelist
+ Input: slave mac address
+Output: 0 - execution is correct
+ -1 - there are errors
+ Error handling: Only return value should be checked, errno is not used.
+-----------------------------------------------------------------------------*/
+int create_IGMP_snooping_file(char* mac)
+{
+ int fd_exec_slave = -1, ret = -1;
+ char temp[256];
+ spidlib_igmp_entry_t igmp_entry;
+
+ /* Copy mac address to retrieve it in igmp white list configuration file*/
+ strcpy(igmp_entry.mac_address, mac);
+
+ /* Get status of this slave concerning IGMP snooping*/
+ ret = spidlib_get_igmp_entry(&igmp_entry);
+ if (ret == 0)
+ {
+ /* If slave is enable in igmp.conf, don't write it ebtables rules*/
+ /* to let it subscribed multicast stream */
+ if ( igmp_entry.enable == SPIDLIB_TRUE)
+ return 0;
+ }
+
+ /* In case of this slave is not allowed or not in igmp list, launch rules to
+ avoid any multcast stream subscription; Fill in 88slave */
+ if ((fd_exec_slave = open(SLAVE_FILE, O_WRONLY| O_CREAT | O_APPEND, 0777)) < 0)
+ return -1;
+
+ write(fd_exec_slave, "\n#IGMP Snooping\n",16);
+ memset(temp, 0, sizeof(temp));
+ sprintf(temp, "ebtables -A FORWARD -p 0x0800 --ip-protocol 2 -j DROP\nebtables -A FORWARD -p 0x8100 --vlan-encap 0x0800 --vlan-ip-proto 2 -j DROP\n");
+ write(fd_exec_slave, temp, strlen(temp));
+
+ close (fd_exec_slave);
+ return 0;
+}
+
+/*-----------------------------------------------------------------------------
+ Function: get_slave_config
+ Description: Get type of service, ports number and slave_index
+ for this slave.
+ Input: slave mac address
+ number of ports for this slave
+ type of service (vprio, vid or tos)
+ index of slave
+ Output: 0 - execution is correct
+ -1 - there are errors
+ Error handling: Only return value should be checked, errno is not used.
+-----------------------------------------------------------------------------*/
+int get_slave_config(char* mac, spidlib_service_type_t* service_type, int* slave_index)
+{
+ spidlib_whitelist_entry_t wl_entry;
+
+ if ((NULL == mac )
+ || (NULL == service_type)
+ || (NULL == slave_index)) return -1;
+
+ // Get type of service
+ if (spidlib_get_service_type(service_type) < 0 ) return -1;
+
+ // Get index of this slave by its mac address, from whitelist
+ if (spidlib_get_whitelist_entry(mac, &wl_entry ) < 0) return -1;
+ *slave_index = wl_entry.tei;
+
+ return 0;
+}
+
+
+/*int main(int argc, char** argv)*/
+int config_creator(char* mac_address, unsigned int * nb_eth_ports, unsigned int* architecture)
+{
+ // int nb_ports;
+ spidlib_service_type_t service_type;
+ int slave_index;
+ char path[256];
+ int vid_tag =NO_VLAN_TAG_ASKED; // this value shows that no tag with mark_id is asked
+
+ /* check architecture */
+ if (*architecture != SPIDLIB_ARCHI_SPC200E && *architecture != SPIDLIB_ARCHI_SPC200C)
+ {
+ syslog(LOG_WARNING, "config_creator: architecture not supported");
+ return -1;
+ }
+
+ if (!spidlib_is_str_mac(mac_address))
+ {
+ syslog(LOG_WARNING, "config_creator: mac address provided isn't a real mac address");
+ return -1;
+ }
+
+ if (remove_temp_dir() != 0)
+ return -1;
+
+ if (create_temp_dir() != 0)
+ return -1;
+
+ if (get_slave_config(mac_address, &service_type, &slave_index ) != 0)
+ {
+ syslog(LOG_WARNING, "config_creator: cannot get slave configuration (number of ports, type of service...) for : %s \n", mac_address);
+ return -1;
+ }
+
+ /* write the file common header */
+ {
+ int fd;
+ if ((fd = open (SLAVE_FILE, O_WRONLY| O_CREAT, 0777)) >= 0)
+ {
+ write (fd, "#!/bin/sh\n", 10);
+ close (fd);
+ }
+ }
+
+ if (create_broadcast_file() != 0)
+ syslog(LOG_WARNING, "config_creator: couldn't create broadcast restriction file for slave %s\n", mac_address);
+
+ if (create_unknown_file() != 0)
+ syslog(LOG_WARNING, "config_creator: couldn't create unknown restriction file for slave %s\n", mac_address);
+
+ if (create_RF_output_level_all_file(mac_address, architecture) != 0)
+ syslog(LOG_WARNING, "config_creator: couldn't create RF_output_level file for slave %s\n", mac_address);
+
+ if (create_mac_limitation_all_file() != 0)
+ syslog(LOG_WARNING, "config_creator: couldn't create mac limitation file for slave %s\n", mac_address);
+
+ // Tag vlan from eth to plc
+ // Method depends on number of ports :
+ if (*nb_eth_ports ==1)
+ {
+ if (tag_vlan_one_port(&service_type, &slave_index, &vid_tag) != 0)
+ syslog(LOG_WARNING, "config_creator: couldn't create rules for one port for tagging vlan for slave %s\n", mac_address);
+ }
+ else if (*nb_eth_ports != 0)
+ {
+ if (tag_vlan_several_ports(nb_eth_ports, &service_type, &slave_index, &vid_tag) != 0)
+ syslog(LOG_WARNING, "config_creator: couldn't create rules for several ports for tagging vlan for slave %s\n", mac_address);
+ }
+ else {syslog(LOG_WARNING, "config_creator: number of ports for slave %s is null \n", mac_address);}
+
+
+ if (create_QOS_prio_file(nb_eth_ports, &service_type, &slave_index, &vid_tag) != 0)
+ syslog(LOG_WARNING, "config_creator: couldn't create QOS_prio rules for slave %s\n", mac_address);
+
+ if (create_DBA_PIR_file(nb_eth_ports, &service_type, &slave_index, &vid_tag) != 0)
+ syslog(LOG_WARNING, "config_creator: couldn't create DBA PIR rules for slave %s\n", mac_address);
+
+ /*IGMP snooping management*/
+ if (create_IGMP_snooping_file(mac_address) != 0)
+ syslog(LOG_WARNING, "config_creator: couldn't create IGMP Snooping rules for slave %s\n", mac_address);
+
+
+ /* Check if no config file for this slave still exists*/
+ /* If it still exists, untar it to keep previous configuration*/
+ /* This could complete automatic configuration*/
+ if (!find_config(mac_address, path))
+ if (untar_config(path) != 0)
+ return -1;
+
+ if (tar_config(mac_address) != 0)
+ {
+ syslog(LOG_WARNING, "config_creator: cannot execute \"tar_config\" for slave = %s \n", mac_address);
+ return -1;
+ }
+
+ /* if (remove_temp_dir() != 0)
+ return -1; */ //MRA
+
+ return 0;
+}
+
diff --git a/polux/application/ndd/src/ndd.c b/polux/application/ndd/src/ndd.c
new file mode 100644
index 0000000000..a5273004f1
--- /dev/null
+++ b/polux/application/ndd/src/ndd.c
@@ -0,0 +1,488 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <syslog.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <linux/netlink.h>
+#include <sys/stat.h>
+
+#include "ioctl-interface.h"
+#include "smmlib.h"
+#include "image_desc.h"
+#include "spidlib.h"
+
+#define MAX_PAYLOAD 1024
+
+#define EXTENSION_CONFIG ".tar"
+
+extern int config_creator(char* mac_address, unsigned int * nb_eth_ports, unsigned int * architecture);
+
+typedef struct
+{
+ char mac_slave[18];
+ char version[64];
+ char archi[16];
+ unsigned int result_update;
+ unsigned int result_config;
+ unsigned int epoch_time;
+ unsigned int eth_port;
+ unsigned int update_asked;
+ char file_update[256];
+
+} conf_data_t;
+
+#define STRUCT_FIELDS 9
+
+int get_file_from_path(const char* full_path, char* file)
+{
+ int i = 0, last_pos = -1;
+ for (i = 0; i< strlen(full_path); i++)
+ if (full_path[i] == '/')
+ last_pos = i;
+
+ if (last_pos == -1)
+ strcpy(file, full_path);
+ else
+ strcpy(file, &full_path[last_pos + 1]);
+
+ return 0;
+}
+
+int create_socket()
+{
+ struct sockaddr_nl src_addr;
+ int fd;
+
+ if ((fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_MIB)) < 0){
+ syslog(LOG_WARNING, "ndd: socket error");
+ return -1;
+ }
+
+ memset(&src_addr, 0, sizeof(src_addr));
+ src_addr.nl_family = AF_NETLINK;
+ src_addr.nl_pid = getpid(); /* self pid */
+ src_addr.nl_groups = 1; /* mcast group 1<<0 */
+
+ if (bind(fd, (struct sockaddr *) &src_addr, sizeof(src_addr)) < 0){
+ syslog(LOG_WARNING, "ndd: bind error");
+ close (fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+int ftp_get_file(const char* ip_address, uint16_t port, const char* login, const char* passwd, const char* file_retrieve, const char* file_store)
+{
+ FILE* message;
+ char command[256];
+
+ syslog(LOG_DEBUG, "ndd: retrieving slave image %s on server %s, port %d, login %s, password %s", file_retrieve, ip_address, port, login, passwd);
+
+ sprintf(command, "ftpget -u %s -p %s %s -P %d %s %s", login, passwd, ip_address, port, file_store, file_retrieve);
+ message = spidlib_popen(command, "r");
+
+ return spidlib_pclose(message);
+}
+
+int download_file(const char* file_to_download, const char* file_to_store)
+{
+ char infos[4][32];
+ unsigned int port = 0;
+ if (spidlib_read_config_param(SPIDLIB_CONFIG_SYSTEM_PATH, SPIDLIB_ADMIN_FTP_ADDRESS_KEY, infos[0], 32))
+ {
+ syslog(LOG_WARNING, "ndd: error retrieving the FTP address to use to download the file");
+ return -1;
+ }
+ if (spidlib_read_config_param(SPIDLIB_CONFIG_SYSTEM_PATH, SPIDLIB_ADMIN_FTP_PORT_KEY, infos[1], 32))
+ {
+ syslog(LOG_WARNING, "ndd: error retrieving the FTP port to use to download the file");
+ return -1;
+ }
+ if (spidlib_read_config_param(SPIDLIB_CONFIG_SYSTEM_PATH, SPIDLIB_ADMIN_FTP_LOGIN_KEY, infos[2], 32))
+ {
+ syslog(LOG_WARNING, "ndd: error retrieving the FTP login to use to download the file");
+ return -1;
+ }
+ if (spidlib_read_config_param(SPIDLIB_CONFIG_SYSTEM_PATH, SPIDLIB_ADMIN_FTP_PASSWORD_KEY, infos[3], 32))
+ {
+ syslog(LOG_WARNING, "ndd: error retrieving the FTP password to use to download the file");
+ return -1;
+ }
+
+ if (sscanf(infos[1], "%u", &port) != 1)
+ {
+ syslog(LOG_WARNING, "ndd: error with the FTP port, unknown format");
+ return -1;
+ }
+
+ return ftp_get_file(infos[0], port, infos[2], infos[3], file_to_download, file_to_store);
+}
+
+int get_data_in_conf(const char* mac, conf_data_t* new_config)
+{
+ unsigned int elt_number = STRUCT_FIELDS - 1;
+ char buffer[SPIDLIB_LINE_MAX_LEN];
+ char *elt_buffer[STRUCT_FIELDS - 1];
+
+ if ((mac == NULL)|| (new_config == NULL))
+ return -1;
+
+ strcpy(new_config->mac_slave, mac);
+
+ if (spidlib_read_line(SPIDLIB_NDD_DATABASE_PATH, " ", new_config->mac_slave, &elt_number, elt_buffer, buffer, SPIDLIB_LINE_MAX_LEN))
+ {
+ syslog(LOG_WARNING, "ndd: error reading the %s line in the ndd list file, new entry?", mac);
+ return -1;
+ }
+
+ strcpy(new_config->mac_slave, mac);
+ strcpy(new_config->version, elt_buffer[0]);
+ strcpy(new_config->archi, elt_buffer[1]);
+ sscanf(elt_buffer[2], "%d", &(new_config->result_update));
+ sscanf(elt_buffer[3], "%d", &(new_config->result_config));
+ sscanf(elt_buffer[4], "%d", &(new_config->epoch_time));
+ sscanf(elt_buffer[5], "%d", &(new_config->eth_port));
+ sscanf(elt_buffer[6], "%d", &(new_config->update_asked));
+ strcpy(new_config->file_update, elt_buffer[7]);
+
+ return 0;
+}
+
+int get_default_data(const char* mac, conf_data_t* new_config)
+{
+ if ((mac == NULL)|| (new_config == NULL))
+ return -1;
+
+ strcpy(new_config->mac_slave, mac);
+ strcpy(new_config->version, "unknown");
+ strcpy(new_config->archi, "unknown");
+ new_config->result_update = 0;
+ new_config->result_config = 0;
+ new_config->epoch_time = 0;
+ new_config->eth_port = 1;
+ new_config->update_asked = 0;
+ strcpy(new_config->file_update, "no_file");
+
+ return 0;
+}
+
+int write_data_in_conf(conf_data_t* new_config)
+{
+ if (new_config == NULL)
+ return -1;
+
+ char temp0[16];
+ char temp1[16];
+ char* list_res[STRUCT_FIELDS - 1];
+ syslog(LOG_DEBUG, "eth_port retrieved: %d", new_config->eth_port);
+ sprintf(temp0, "%d", new_config->epoch_time);
+ if (new_config->eth_port == SPIDLIB_ETH_PORT_NUMBER_UNKNOWN)
+ sprintf(temp1, "unknown");
+ else
+ sprintf(temp1, "%d", new_config->eth_port);
+
+ list_res[0] = new_config->version;
+ list_res[1] = new_config->archi;
+ list_res[2] = (new_config->result_update?"0":"1");
+ list_res[3] = (new_config->result_config?"0":"1");
+ list_res[4] = temp0;
+ list_res[5] = temp1;
+ list_res[6] = (new_config->update_asked?"1":"0");;
+ list_res[7] = new_config->file_update;
+
+ return spidlib_write_line(SPIDLIB_NDD_DATABASE_PATH, ' ', new_config->mac_slave, STRUCT_FIELDS - 1, list_res);
+}
+
+int slave_detected(char* mac_slave)
+{
+ conf_data_t slave_config;
+ struct timeval tv;
+ unsigned int current_index;
+ unsigned int architecture;
+ unsigned int eth_port;
+ smm_image_info_t info[2];
+ int ret, i, auto_config_enabled = 0, auto_update_enabled = 0;
+ char mac_str_lower[18], mac_str_upper[18];
+ char mac_str_corrected_lower[18], mac_str_corrected_upper[18];
+ char enabled[16];
+
+ /* Let the slave finish its boot, otherwise messages might be corrupted, ugly hack */
+ sleep(5);
+
+ /* Retrieve auto-update/config state */
+ memset(enabled, 0, sizeof(enabled));
+ ret = spidlib_read_config_param(SPIDLIB_CONFIG_SYSTEM_PATH, SPIDLIB_AUTOUPDATE_KEY, enabled, sizeof(enabled));
+ if (SPIDLIB_GET_BOOLEAN(enabled) || ret != 0)
+ auto_update_enabled = 1;
+ memset(enabled, 0, sizeof(enabled));
+ ret = spidlib_read_config_param(SPIDLIB_CONFIG_SYSTEM_PATH, SPIDLIB_AUTOCONF_KEY, enabled, sizeof(enabled));
+ if (SPIDLIB_GET_BOOLEAN(enabled) || ret != 0)
+ auto_config_enabled = 1;
+
+ /* Get the time of the slave arrival on the network */
+ gettimeofday(&tv, NULL);
+
+ /* Fill the mac address in lower case */
+ if (spidlib_mac_bin_to_str_lower(mac_slave, mac_str_lower))
+ {
+ syslog(LOG_WARNING, "ndd: error spidlib mac_bin_to_str");
+ return -1;
+ }
+ /* Fill the mac address in upper case */
+ if (spidlib_mac_bin_to_str_upper(mac_slave, mac_str_upper))
+ {
+ syslog(LOG_WARNING, "ndd: error spidlib mac_bin_to_str");
+ return -1;
+ }
+
+ /* get previous conf for this entry in the ndd list or get a default one */
+ if (get_data_in_conf(mac_str_lower, &slave_config))
+ get_default_data(mac_str_lower, &slave_config);
+
+ /* Fill slave struct with new time */
+ slave_config.epoch_time = tv.tv_sec;
+
+ /* Retrieve the actual version on the slave, its architecture and the number of eth ports */
+ if ((ret = smm_image_info(mac_slave, 3, &current_index, &architecture, &eth_port, info)) != 0)
+ {
+ syslog(LOG_WARNING, "ndd: couldn't retrieve version on slave, setting to unknown, error %d", ret);
+ strcpy(slave_config.version, "Unknown");
+ strcpy(slave_config.archi, "Unknown");
+ slave_config.eth_port = SPIDLIB_ETH_PORT_NUMBER_UNKNOWN;
+ }
+ else
+ {
+ strcpy(slave_config.version, info[current_index & 0x1].version);
+ /* Get archi of slave */
+ if (architecture == SPIDLIB_ARCHI_SPC200E)
+ strcpy(slave_config.archi, "spc200e");
+ else if (architecture == SPIDLIB_ARCHI_SPC200C)
+ strcpy(slave_config.archi, "spc200c");
+ else
+ strcpy(slave_config.archi, "Unknown");
+ slave_config.eth_port = eth_port;
+ }
+
+ /* Retrieve the slave entry in the whitelist */
+ spidlib_whitelist_entry_t temp_whitelist;
+ memset(&temp_whitelist, 0, sizeof(spidlib_whitelist_entry_t));
+ if (spidlib_get_whitelist_entry (mac_str_lower, &temp_whitelist) != 0)
+ {
+ syslog(LOG_DEBUG, "ndd: no entry for %s in the whitelist", mac_str_lower);
+ }
+
+ /* Auto-update part, check auto-update is selected in the config file, that an update has been requested for this particular slave and that auto-update is allowed in the whitelist */
+ if (auto_update_enabled && slave_config.update_asked && temp_whitelist.autoUpgrade_en == SPIDLIB_TRUE)
+ {
+ /* Launch and get the result of the update */
+ int archi = 0;
+ char path_image[256];
+ char file_image[256];
+
+ /* Create directory that will contain the images if not already done */
+ mkdir(SPIDLIB_TEMP_SLAVE_IMAGE_DIRECTORY, 755);
+
+ /* Get filename from the provided path */
+ get_file_from_path(slave_config.file_update, file_image);
+
+ /* Prepare path where the image is stored */
+ sprintf(path_image, "%s/%s", SPIDLIB_TEMP_SLAVE_IMAGE_DIRECTORY, file_image);
+
+ /* If image is not there for any reason, erase previous one(s) and download it */
+ struct stat infos;
+ if (stat(path_image, &infos))
+ {
+ char command_erase[256];
+ sprintf(command_erase, "rm -rf %s/*", SPIDLIB_TEMP_SLAVE_IMAGE_DIRECTORY);
+ if (spidlib_system(command_erase) != 0)
+ {
+ syslog(LOG_WARNING, "ndd: error erasing previous files");
+ }
+ if (download_file(slave_config.file_update, path_image))
+ {
+ syslog(LOG_WARNING, "ndd: error uploading the new file");
+ }
+ }
+
+ /* Launch and get the result of the update */
+ slave_config.result_update = smm_update_file(mac_slave, 3, path_image, &archi);
+
+ /* Reset update_asked info*/
+ slave_config.update_asked = 0;
+ }
+ else
+ syslog(LOG_WARNING, "ndd: no update asked or autoupdate disabled ");
+
+ /* Auto-config part */
+ if (auto_config_enabled)
+ {
+ char path_mac_tar_lower[256], path_mac_tar_upper[256];
+
+ /*Launch config creator*/
+ if (config_creator(mac_str_lower, &eth_port, &architecture) < 0)
+ {
+ syslog(LOG_WARNING, "ndd: error exec. config_creator for slave : %s",mac_str_lower);
+ }
+
+ /* Change : to _ in the mac_address for the file name of the config file to look for in lower case */
+ memcpy(mac_str_corrected_lower, mac_str_lower, 18);
+ for (i = 0; i < strlen(mac_str_corrected_lower); i++)
+ if (mac_str_corrected_lower[i] == ':')
+ mac_str_corrected_lower[i] = '_';
+
+ /* Change : to _ in the mac_address for the file name of the config file to look for in upper case */
+ memcpy(mac_str_corrected_upper, mac_str_upper, 18);
+ for (i = 0; i < strlen(mac_str_corrected_upper); i++)
+ if (mac_str_corrected_upper[i] == ':')
+ mac_str_corrected_upper[i] = '_';
+
+ /* Launch and get the result of the config */
+ sprintf(path_mac_tar_lower, "%s/%s%s", SPIDLIB_AUTOCONF_PATH, mac_str_corrected_lower, EXTENSION_CONFIG);
+ sprintf(path_mac_tar_upper, "%s/%s%s", SPIDLIB_AUTOCONF_PATH, mac_str_corrected_upper, EXTENSION_CONFIG);
+
+ slave_config.result_config = smm_config_file(mac_slave, 3, path_mac_tar_lower);
+ /* SMM_EREFU means update refused by slave because he already hads the config, so no need to send others */
+ if (slave_config.result_config != 0 && slave_config.result_config != -SMM_EREFU)
+ {
+ slave_config.result_config = smm_config_file(mac_slave, 3, path_mac_tar_upper);
+ if (slave_config.result_config != 0 && slave_config.result_config != -SMM_EREFU)
+ {
+ slave_config.result_config = smm_config_file(mac_slave, 3, SPIDLIB_DEFAULT_CONFIG_PATH);
+ }
+ }
+ }
+ else
+ syslog(LOG_WARNING, "ndd: autoconfig disabled");
+
+ /* Reboot slave if auto-config was enabled and config was uploaded successfully on slave */
+ if (auto_config_enabled && !slave_config.result_config)
+ {
+ if (smm_reboot(mac_slave, 3) != 0)
+ syslog(LOG_WARNING, "ndd: slave %s accepted config, but didn't reboot", mac_str_lower);
+ else
+ syslog(LOG_INFO, "ndd: slave %s accepted config and has been rebooted", mac_str_lower);
+ }
+
+ return write_data_in_conf(&slave_config);
+}
+
+int main(int argc, char** argv)
+{
+ int fd, ret, ff;
+ struct msghdr msg;
+ struct iovec iov;
+ union mib_event *event;
+ struct sockaddr_nl dest_addr;
+ struct nlmsghdr *nlh = NULL;
+ char mac_slave[18];
+ fd_set rset;
+
+ /* Create database file if not already present */
+ if ((ff = open(SPIDLIB_NDD_DATABASE_PATH, O_CREAT | O_WRONLY | O_TRUNC , S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0)
+ {
+ syslog(LOG_WARNING, "ndd: couldn't open or create the database in %s", SPIDLIB_NDD_DATABASE_PATH);
+ return -1;
+ }
+
+ close(ff);
+
+ /* Create /usr/local/autoconf is not already done */
+ mkdir(SPIDLIB_AUTOCONF_PATH, 755);
+
+ /* Create socket */
+ if((fd = create_socket()) < 0)
+ {
+ syslog(LOG_WARNING, "ndd: socket not created, abort");
+ return -1;
+ }
+
+#if 0
+ /* Set socket reception buffer to 5k, testonly, to be removed for release */
+ int buffsize = 5*1024;
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &buffsize, sizeof(buffsize)) != 0)
+ {
+ syslog(LOG_WARNING, "ndd: couldn't change socket reception buffer size");
+ }
+#endif
+
+
+ /* Read message from kernel */
+ nlh=(struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_PAYLOAD));
+ memset(nlh, 0, NLMSG_SPACE(MAX_PAYLOAD));
+ memset(&dest_addr, 0, sizeof(dest_addr));
+ iov.iov_base = (void *)nlh;
+ iov.iov_len = NLMSG_SPACE(MAX_PAYLOAD);
+ msg.msg_name = (void *)&dest_addr;
+ msg.msg_namelen = sizeof(dest_addr);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ while (1) {
+ FD_ZERO(&rset);
+ FD_SET(fd, &rset);
+
+ syslog(LOG_INFO, "ndd: now waiting for slaves to appear on the network");
+
+ if ((ret = select(fd + 1, &rset, NULL, NULL, NULL)) < 0)
+ {
+ syslog(LOG_WARNING, "ndd: select error (errno = %d)\n", errno);
+ close(fd);
+ return -1;
+ }
+
+ ret = recvmsg(fd, &msg, 0);
+ if (ret < 0 && errno == ENOBUFS)
+ {
+ syslog(LOG_WARNING, "ndd: recvmsg error (errno = %d), closing and reopening socket\n", errno);
+ close(fd);
+ if((fd = create_socket()) < 0)
+ {
+ syslog(LOG_WARNING, "ndd: socket not created, abort");
+ return -1;
+ }
+ }
+ else if (ret < 0)
+ {
+ syslog(LOG_WARNING, "ndd: recvmsg unknown error (errno = %d), abort\n", errno);
+ return -1;
+ }
+ else
+ {
+ event = (union mib_event*)NLMSG_DATA(nlh);
+
+ if (event == NULL)
+ {
+ syslog(LOG_WARNING, "ndd: event received is null");
+ }
+ else
+ {
+ if (event->event_id == EV_HOST_UP)
+ {
+ if (spidlib_mac_bin_to_str_lower(event->host_up.mac_addr, mac_slave))
+ {
+ syslog(LOG_WARNING, "ndd: error spidlib mac_bin_to_str");
+ close(fd);
+ return -1;
+ }
+ if (slave_detected(event->host_up.mac_addr))
+ {
+ syslog(LOG_INFO, "ndd: slave with mac %s appeared but error registrering it to the database file", mac_slave);
+ }
+ }
+ }
+ }
+ }
+
+ free(nlh);
+ close(fd);
+
+ return 0;
+}