// This file is part of "NPR70 modem firmware" software
// (A GMSK data modem for ham radio 430-440MHz, at several hundreds of kbps)
// Copyright (c) 2017-2020 Guillaume F. F4HDK (amateur radio callsign)
//
// "NPR70 modem firmware" is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// "NPR70 modem firmware" is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with "NPR70 modem firmware". If not, see
#include "DHCP_ARP.h"
#include "mbed.h"
#include "global_variables.h"
#include "Eth_IPv4.h"
#include "W5500.h"
#include "HMI_telnet.h"
// Content : DHCP server (for TDMA clients and point to point config)
// ARP proxy for "bridged ethernet" emulation.
// ARP resolution (a little bit ugly)
#define DHCP_ARP_tab_size 32
#define DHCP_ARP_timeout 360 /*120*/
static unsigned char DHCP_ARP_MAC[DHCP_ARP_tab_size][6];
static unsigned long int DHCP_ARP_IP[DHCP_ARP_tab_size];
static unsigned char DHCP_ARP_status[DHCP_ARP_tab_size]; // DHCP ARP
// 0: Free Free
// 1: allocation in progress (unused)
// 2: allocated Valid
// 3: prefered but not allocated Timeout_1
static unsigned int DHCP_ARP_date[DHCP_ARP_tab_size];
int compare_IP(unsigned char* IP1, unsigned char* IP2) {
int result;
int i;
result = 1;
for (i=0; i<4; i++) {
if (IP1[i] != IP2[i]) {
result = 0;
}
}
return result;
}
int compare_MAC(unsigned char* MAC1, unsigned char* MAC2) {
int result;
int i;
result = 1;
for (i=0; i<6; i++) {
if (MAC1[i] != MAC2[i]) {
result = 0;
}
}
return result;
}
// *** DHCP functions ***
void reset_DHCP_table(LAN_conf_T* LAN_config) {
int i;
for (i=0; iDHCP_alloc_MAC[i], client_MAC), i);
}
//printf ("previous entry with same MAC: %i\r\n", i_previous_alloc);
// 1.2)check if matches previous allocation
if (i_previous_alloc!=255) {
if (DHCP_ARP_IP[i_previous_alloc] == req_IP_int) {
match_previous_alloc = 1;
} else {
match_previous_alloc = 0;
}
if (match_previous_alloc==1) {// matches previous allocation
// total match with previous allocation
answer = 1;
if (req_type==1) {
DHCP_ARP_status[i_previous_alloc] = 1;
}
if (req_type==3) {
DHCP_ARP_status[i_previous_alloc] = 2;
DHCP_ARP_date[i_previous_alloc] = GLOBAL_timer.read_us();
}
} else {
//address requested different from previous allocation
}
//printf ("req matches previous:%i\r\n", match_previous_alloc);
}
// 1.3) check if requested IP agreed : inside range
int requ_IP_inside_range;
if ( (req_IP_int >= LAN_config->DHCP_range_start) && (req_IP_int < (LAN_config->DHCP_range_start + LAN_config->DHCP_range_size)) ) {
requ_IP_inside_range = 1;
} else {
requ_IP_inside_range = 0;
}
//printf("requested ip inside range:%i\r\n", requ_IP_inside_range);
// 1.4) chech if requested IP is free (not allocated to another MAC)
int req_IP_free = 1;
for (i=0; iDHCP_range_size - 1); i>=0; i--) {
IP_tested = LAN_config->DHCP_range_start + i;
OK_loc=1;
for (j=0; j=0; i--) {
if (DHCP_ARP_status[i]!=2) {
free_DHCP_slot = i;
}
//printf("dhcp inside lookfor:%i stat:%i\r\n", i, LAN_config->DHCP_alloc_status[i]);
}
if (free_DHCP_slot!=255) {
DHCP_ARP_status[free_DHCP_slot] = new_status;
if (new_status == 2) {DHCP_ARP_date[free_DHCP_slot] = GLOBAL_timer.read_us();}
DHCP_ARP_IP[free_DHCP_slot] = IP_char2int(proposed_IP);
for (i=0; i<6; i++) {
DHCP_ARP_MAC[free_DHCP_slot][i] = client_MAC[i];
}
//printf("new DHCP entry OK, index %i status %i \r\n", free_DHCP_slot, new_status);
}
}
return answer;
}
void DHCP_server(LAN_conf_T* LAN_config, W5500_chip* W5500 ) {
static unsigned char RX_data[600];//600
unsigned char client_MAC[7];
unsigned char session_ID[5];
unsigned char requested_IP[5]={0,0,0,0,0};
unsigned char DHCP_server_IP[5]={0,0,0,0,0};
int RX_size, size_UDP, i;
unsigned char message_type_client = 0;
unsigned char message_type_server;
int index_opt_answer=240;
static unsigned char DHCP_answer[400];
unsigned char proposed_IP[4];
int loc_status;
RX_size = W5500_read_received_size(W5500, 3);
if (RX_size>0) {
size_UDP = W5500_read_UDP_pckt(W5500, 3, RX_data);
if (RX_data[8]==1) { // Valid DHCP request
// *** DHCP request decoding ***
// client mac address read
for (i=0; i<6; i++) {
client_MAC[i] = RX_data[i+36];
//printf("%x:", client_MAC[i]);
}
// session ID (XID)
for (i=0; i<4; i++) {
session_ID[i] = RX_data[i+12];
//printf("%x:", session_ID[i]);
}
for (i=0; i<4; i++) {
requested_IP[i] = RX_data[i+20];
}
//printf("\r\n");
//printf("\r\nDHCP from client RXs:%d UDPs:%d\r\n", RX_size, size_UDP);
// DHCP option read
int option_pos;
unsigned char option_size;
unsigned char option_type;
option_pos = 248;
do {
option_type = RX_data[option_pos];
option_size = RX_data[option_pos+1];
//printf("option:%d\r\n",option_type);
switch (option_type) {
case 53 : message_type_client = RX_data[option_pos+2]; break;
case 50 :
requested_IP[0] = RX_data[option_pos+2];
requested_IP[1] = RX_data[option_pos+3];
requested_IP[2] = RX_data[option_pos+4];
requested_IP[3] = RX_data[option_pos+5];
break;
case 54 :
DHCP_server_IP[0] = RX_data[option_pos+2];
DHCP_server_IP[1] = RX_data[option_pos+3];
DHCP_server_IP[2] = RX_data[option_pos+4];
DHCP_server_IP[3] = RX_data[option_pos+5];
break;
}
option_pos = option_pos + option_size + 2;
} while ((option_type != 255) && (option_pos < size_UDP));
// independant DHCP answer fields
for (i=0; i<400; i++ ) {
DHCP_answer[i] = 0;
}
DHCP_answer[0] = 0x02;//answer
DHCP_answer[1] = 0x01;
DHCP_answer[2] = 0x06;
DHCP_answer[3] = 0x00;
for (i=0; i<4; i++) {// session ID
DHCP_answer[i+4] = session_ID[i];
}
for (i=0; i<6; i++) {// client MAC
DHCP_answer[i+28] = client_MAC[i];
}
DHCP_answer[236] = 0x63;//magic cookie
DHCP_answer[237] = 0x82;
DHCP_answer[238] = 0x53;
DHCP_answer[239] = 0x63;
// decision
loc_status = lookfor_free_LAN_IP (LAN_config, client_MAC, requested_IP, proposed_IP, message_type_client);
if (message_type_client == 1) { // discover -> offer
for (i=0; i<4; i++) { //proposed IP
DHCP_answer[i+16] = proposed_IP[i];
}
message_type_server = 2;
DHCP_answer[index_opt_answer]=53; // message type
DHCP_answer[index_opt_answer+1]=1;
DHCP_answer[index_opt_answer+2]=message_type_server;
index_opt_answer = index_opt_answer +3;
DHCP_answer[index_opt_answer]=1; // subnet mask
DHCP_answer[index_opt_answer+1]=4;
IP_int2char (LAN_config->LAN_subnet_mask, (DHCP_answer + index_opt_answer + 2) );
index_opt_answer = index_opt_answer +6;
if (LAN_config->LAN_def_route_activ == 1) {
DHCP_answer[index_opt_answer]=3; // default route
DHCP_answer[index_opt_answer+1]=4;
IP_int2char (LAN_config->LAN_def_route, (DHCP_answer + index_opt_answer + 2) );
index_opt_answer = index_opt_answer +6;
}
if (LAN_config->LAN_DNS_activ == 1) {
DHCP_answer[index_opt_answer]=6; // DNS server
DHCP_answer[index_opt_answer+1]=4;
IP_int2char (LAN_config->LAN_DNS_value, (DHCP_answer + index_opt_answer + 2) );
index_opt_answer = index_opt_answer +6;
}
DHCP_answer[index_opt_answer]=51; //Lease Time
DHCP_answer[index_opt_answer+1]=4;
DHCP_answer[index_opt_answer+2]=0x00;
DHCP_answer[index_opt_answer+3]=0x00;//01
DHCP_answer[index_opt_answer+4]= (DHCP_ARP_timeout & 0xFF00) >> 8;//0x00
//DHCP_answer[index_opt_answer+5]=0x3C;//80
DHCP_answer[index_opt_answer+5]= DHCP_ARP_timeout & 0xFF;//DHCP_ARP_timeout
index_opt_answer = index_opt_answer +6;
DHCP_answer[index_opt_answer]=54; // DHCP server IP
DHCP_answer[index_opt_answer+1]=4;
IP_int2char (LAN_config->LAN_modem_IP, (DHCP_answer + index_opt_answer + 2) );
index_opt_answer = index_opt_answer + 6;
//DHCP_answer[index_opt_answer]=26; // Interface MTU
//DHCP_answer[index_opt_answer+1]=2;
//DHCP_answer[index_opt_answer+2]=0x03;//02
//DHCP_answer[index_opt_answer+3]=0xE8;//40
//index_opt_answer = index_opt_answer + 4;
//DHCP_answer[index_opt_answer]=27; // subnets local
//DHCP_answer[index_opt_answer+1]=1;
//DHCP_answer[index_opt_answer+2]=0x01;
//index_opt_answer = index_opt_answer + 3;
DHCP_answer[index_opt_answer]=255;// end
index_opt_answer = index_opt_answer + 1;
W5500_write_TX_buffer(W5500, 3, DHCP_answer, index_opt_answer, 1);
}
if ( (message_type_client == 3) && (loc_status !=0 ) ) { // request -> ack
for (i=0; i<4; i++) { //proposed IP
DHCP_answer[i+16] = proposed_IP[i];
}
message_type_server = 5;
DHCP_answer[index_opt_answer]=53; // message type
DHCP_answer[index_opt_answer+1]=1;
DHCP_answer[index_opt_answer+2]=message_type_server;
index_opt_answer = index_opt_answer +3;
DHCP_answer[index_opt_answer]=1; // subnet mask
DHCP_answer[index_opt_answer+1]=4;
IP_int2char (LAN_config->LAN_subnet_mask, (DHCP_answer + index_opt_answer + 2) );
index_opt_answer = index_opt_answer +6;
if (LAN_config->LAN_def_route_activ == 1) {
DHCP_answer[index_opt_answer]=3; // default route
DHCP_answer[index_opt_answer+1]=4;
IP_int2char (LAN_config->LAN_def_route, (DHCP_answer + index_opt_answer + 2) );
index_opt_answer = index_opt_answer +6;
}
if (LAN_config->LAN_DNS_activ == 1) {
DHCP_answer[index_opt_answer]=6; // DNS server
DHCP_answer[index_opt_answer+1]=4;
IP_int2char (LAN_config->LAN_DNS_value, (DHCP_answer + index_opt_answer + 2) );
index_opt_answer = index_opt_answer +6;
}
DHCP_answer[index_opt_answer]=51; //Lease Time
DHCP_answer[index_opt_answer+1]=4;
DHCP_answer[index_opt_answer+2]=0x00;
DHCP_answer[index_opt_answer+3]=0x00;//01
DHCP_answer[index_opt_answer+4]= (DHCP_ARP_timeout & 0xFF00) >> 8;//51
DHCP_answer[index_opt_answer+5]= DHCP_ARP_timeout & 0xFF;
index_opt_answer = index_opt_answer +6;
DHCP_answer[index_opt_answer]=54; // DHCP server IP
DHCP_answer[index_opt_answer+1]=4;
IP_int2char (LAN_config->LAN_modem_IP, (DHCP_answer + index_opt_answer + 2) );
index_opt_answer = index_opt_answer + 6;
//DHCP_answer[index_opt_answer]=26; // Interface MTU
//DHCP_answer[index_opt_answer+1]=2;
//DHCP_answer[index_opt_answer+2]=0x03;//02
//DHCP_answer[index_opt_answer+3]=0xE8;//40
//index_opt_answer = index_opt_answer + 4;
//DHCP_answer[index_opt_answer]=27; // subnets local
//DHCP_answer[index_opt_answer+1]=1;
//DHCP_answer[index_opt_answer+2]=0x01;
//index_opt_answer = index_opt_answer + 3;
DHCP_answer[index_opt_answer]=255;// end
index_opt_answer = index_opt_answer + 1;
W5500_write_TX_buffer(W5500, 3, DHCP_answer, index_opt_answer, 1);
}
if ( (message_type_client == 3) && (loc_status ==0 ) ) { //request -> NAK
//printf ("NAK NAK\r\n");
message_type_server = 6; // NAK
DHCP_answer[index_opt_answer]=53; // message type
DHCP_answer[index_opt_answer+1]=1;
DHCP_answer[index_opt_answer+2]=message_type_server;
index_opt_answer = index_opt_answer +3;
DHCP_answer[index_opt_answer]=255;// end
index_opt_answer = index_opt_answer + 1;
W5500_write_TX_buffer(W5500, 3, DHCP_answer, index_opt_answer, 1);
}
if ( message_type_client == 7 ) { // DHCP RELEASE
//printf ("DHCP RELEASE\r\n");
DHCP_release (LAN_config, client_MAC);
}
}
}
}
void DHCP_ARP_print_entries(void) {
int i;
unsigned char loc_IP_char[10];
for (i=0; i ");
}
// *** ARP proxy and ARP resolver ***
void ARP_proxy (unsigned char* ARP_req_packet, int size) {
int answer_needed = 0;
int i;
int is_inside_subnet = 0;
int is_inside_client_range = 0;
unsigned long int ARP_client_IP;
unsigned long int ARP_requested_IP;
unsigned char ARP_client_MAC[6];
unsigned char ARP_answ_packet[50] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // dest MAC
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // source MAC
0x08, 0x06, // Ethtype ARP
0x00, 0x01, // HW type Ethernet
0x08, 0x00, // protocol IPv4
0x06, // HW addr size
0x04, // protocol size
0x00, 0x02 // ARP opcode "reply"
};
ARP_client_IP = IP_char2int (ARP_req_packet+28); //in request : client = sender
ARP_requested_IP = IP_char2int (ARP_req_packet+38); //in request : server = target
for (i=0; i<6; i++) {
ARP_client_MAC[i] = ARP_req_packet[22+i]; // in request : client = sender
}
// determines if modem should reply or not
if (ARP_requested_IP != LAN_conf_applied.LAN_modem_IP) { //only replies to non-modem IP
if (is_TDMA_master == 1) {
//TDMA Master (and FDD down) answers to all IP in radio range
if ( (ARP_requested_IP >= CONF_radio_IP_start) && (ARP_requested_IP < (CONF_radio_IP_start + CONF_radio_IP_size) ) ) {
answer_needed = 1;
}
}
else { //TDMA Slave answers to IP inside subnet, but which dont belong to it's own range
if ( (ARP_requested_IP & LAN_conf_applied.LAN_subnet_mask) == (LAN_conf_applied.LAN_modem_IP & LAN_conf_applied.LAN_subnet_mask) ) {
is_inside_subnet = 1;
}
if ( (ARP_requested_IP >= LAN_conf_applied.DHCP_range_start) && (ARP_requested_IP < (LAN_conf_applied.DHCP_range_start + LAN_conf_applied.DHCP_range_size)) ) {
is_inside_client_range = 1;
}
if ( (is_inside_subnet == 1) && (is_inside_client_range == 0) ) {
answer_needed = 1;
}
}
}
if (answer_needed) {
for (i=0; i<6; i++) { // Destination MAC
ARP_answ_packet[i] = ARP_client_MAC[i];
}
for (i=0; i<6; i++) { // Source MAC
ARP_answ_packet[i+6] = CONF_modem_MAC[i];
}
for (i=0; i<6; i++) { // ARP Sender MAC
ARP_answ_packet[i+22] = CONF_modem_MAC[i];
}
//IP_int2char (LAN_config.LAN_modem_IP, (ARP_answ_packet + 28) ); // ARP sender IP
IP_int2char (ARP_requested_IP, (ARP_answ_packet + 28) ); // ARP sender IP
for (i=0; i<6; i++) { // ARP Target MAC
ARP_answ_packet[i+32] = ARP_client_MAC[i];
}
IP_int2char (ARP_client_IP, (ARP_answ_packet + 38) ); //ARP target IP
W5500_write_TX_buffer(W5500_p1, 0, ARP_answ_packet, 42, 0);
}
}
void ARP_client_request (unsigned long int IP_requested) {
unsigned char ARP_packet[50] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // dest MAC
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // source MAC
0x08, 0x06, // Ethtype ARP
0x00, 0x01, // HW type Ethernet
0x08, 0x00, // protocol IPv4
0x06, // HW addr size
0x04, // protocol size
0x00, 0x01 // ARP opcode "request"
};
int i;
for (i=0; i<6; i++) { // source MAC
ARP_packet[i+6] = CONF_modem_MAC[i];
}
for (i=0; i<6; i++) { // ARP sender MAC
ARP_packet[i+22] = CONF_modem_MAC[i];
}
IP_int2char (LAN_conf_applied.LAN_modem_IP, (ARP_packet + 28) ); //ARP sender IP
for (i=0; i<6; i++) { // target MAC : unknown
ARP_packet[i+32] = 0;
}
IP_int2char (IP_requested, (ARP_packet + 38) ); //ARP Target IP
W5500_write_TX_buffer(W5500_p1, 0, ARP_packet, 42, 0);
}
void ARP_client_answer_treatment (unsigned char* ARP_RX_packet, int size) {
unsigned long int ARP_sender_IP;
unsigned long int ARP_target_IP;
unsigned char ARP_sender_MAC[6];
int entry_already_exists = 0;
int i_existing_entry = 0;
int i_free_entry = 255;
int i;
ARP_sender_IP = IP_char2int (ARP_RX_packet+28);
ARP_target_IP = IP_char2int (ARP_RX_packet+38);
for (i=0; i<6; i++) {
ARP_sender_MAC[i] = ARP_RX_packet[22+i];
}
if (ARP_target_IP == LAN_conf_applied.LAN_modem_IP) { //if ARP reply is for us
//first look for existing entry
//printf("ARP reply is for us IP %X\r\n", ARP_sender_IP);
entry_already_exists = 0;
for (i=0; i=0; i--) {
if ( DHCP_ARP_status[i] == 0 ) {
i_free_entry = i;
}
}
if (i_free_entry < DHCP_ARP_tab_size) {// free entry exists
for (i=0; i<6; i++) {
DHCP_ARP_MAC[i_free_entry][i] = ARP_sender_MAC[i];
}
DHCP_ARP_status[i_free_entry] = 2; // valid entry
DHCP_ARP_IP[i_free_entry] = ARP_sender_IP;
DHCP_ARP_date[i_free_entry] = GLOBAL_timer.read_us();
}
}
}
flush_temp_Eth_buffer (ARP_sender_IP);
}
void ARP_RX_packet_treatment (unsigned char* ARP_RX_packet, int size) {
unsigned int ARP_protocol_type;
unsigned int ARP_opcode;
unsigned long int ARP_sender_IP;
unsigned long int ARP_target_IP;
ARP_protocol_type = ARP_RX_packet[16]*0x100 + ARP_RX_packet[17];
ARP_opcode = ARP_RX_packet[20]*0x100 + ARP_RX_packet[21];
if ( (ARP_protocol_type == 0x0800) && (ARP_opcode == 0x0001) ) { // request
//printf ("ARP request\r\n");
ARP_sender_IP = IP_char2int (ARP_RX_packet+28);
ARP_target_IP = IP_char2int (ARP_RX_packet+38);
if (ARP_sender_IP != ARP_target_IP) {//ignores gratuitous ARP request
//printf ("real ARP request\r\n");
ARP_proxy(ARP_RX_packet, size);
}
else {
//printf ("gratuitous!\r\n");
}
}
if ( (ARP_protocol_type == 0x0800) && (ARP_opcode == 0x0002) ) { // reply
//printf ("ARP reply\r\n");
if ( (LAN_conf_applied.DHCP_server_active == 0) || (is_TDMA_master) ){//only active if modem is no DHCP server
ARP_client_answer_treatment (ARP_RX_packet, size);
}
}
}
void DHCP_ARP_periodic_free_table () {
int i;
unsigned int loc_age;
for (i=0; i 1000000 * DHCP_ARP_timeout) ) {
DHCP_ARP_status[i] = 3;
}
}
}
// *** generic functions ***
int lookfor_MAC_from_IP (unsigned char* MAC_out, unsigned long int IP_addr) {
int result = 0;
int i;
int i_found = 300;
unsigned int age_loc=0;
if ( (LAN_conf_applied.DHCP_server_active == 1) && (is_TDMA_master == 0) ) { //resolution for DHCP
i_found = 300;
for (i=0; i