Compare commits

...

5 Commits
v1.0 ... master

Author SHA1 Message Date
Juhani Krekelä bd23f43cfa Notify frotend of Speak version packets 2019-08-04 11:31:07 +03:00
Juhani Krekelä 9a2efcce2e Start working on file sending 2019-08-04 11:16:46 +03:00
Juhani Krekelä ea2523e263 Make test.sh add the veth0a and veth0b interfaces automatically 2019-08-04 11:15:28 +03:00
Juhani Krekelä 605bb9d26f Specify protocol version per-packet. Not used yet, but will be for the file transfer extension 2019-08-01 22:15:21 +03:00
Juhani Krekelä 27a8418f21 Don't create Speak version packets for broadcasts.
This allows new versions of the protocol to send broadcast advertizements
without the older versions telling the sender to speak their version.

Version 1.0 of the reference implementation will still do that, but the
issue is not great enough to warrant pulling and replacing it.
2019-07-28 20:58:57 +03:00
5 changed files with 128 additions and 39 deletions

View File

@ -22,12 +22,13 @@
#include <time.h>
#include <unistd.h>
#define EM_PROTOCOL_VERSION 0
#define EM_PROTOCOL_VERSION 1
#define EM_MESSAGE_MAX_LENGTH (1500 - 2 - 2 - 2)
#define EM_STATUS_BROADCAST_TIME (60 * 1000 + 1000 * random_byte() / 64)
#define EM_RETRANSMIT_TIME (1000 + random_byte() * 2)
#define EM_MAX_RETRANSMIT 5
// Version 0
#define EMT_SPEAK_VERSION 0
#define EMT_STATUS_REQUEST 1
#define EMT_STATUS 2
@ -36,6 +37,9 @@
#define EMT_MESSAGE 5
#define EMT_ACK 6
// Version 1
#define EMT_FILE_OFFER 7
#define EMS_AVAILABLE 0
#define EMS_UNAVAILABLE 1
#define EMS_OFFLINE 2
@ -272,7 +276,7 @@ void send_frame(const unsigned char *frame, size_t frame_length) {
}
}
void write_headers(unsigned char frame[14], const unsigned char destination_mac[6], unsigned char packet_type) {
void write_headers(unsigned char frame[14], const unsigned char destination_mac[6], unsigned char version, unsigned char packet_type) {
// Destination MAC
memcpy(&frame[0], destination_mac, 6);
@ -284,7 +288,7 @@ void write_headers(unsigned char frame[14], const unsigned char destination_mac[
frame[13] = 0x7a;
// Ethermess version
frame[14] = EM_PROTOCOL_VERSION;
frame[14] = version;
// Ethermess packet type
frame[15] = packet_type;
@ -293,7 +297,7 @@ void write_headers(unsigned char frame[14], const unsigned char destination_mac[
void send_speak_version(const unsigned char destination[6]) {
unsigned char frame[14 + 2 + 1];
write_headers(frame, destination, EMT_SPEAK_VERSION);
write_headers(frame, destination, 0, EMT_SPEAK_VERSION);
// Version to speak
frame[16] = EM_PROTOCOL_VERSION;
@ -304,7 +308,7 @@ void send_speak_version(const unsigned char destination[6]) {
void send_status_request(const unsigned char destination[6]) {
unsigned char frame[14 + 2];
write_headers(frame, destination, EMT_STATUS_REQUEST);
write_headers(frame, destination, 0, EMT_STATUS_REQUEST);
send_frame(frame, sizeof(frame));
}
@ -312,7 +316,7 @@ void send_status_request(const unsigned char destination[6]) {
void send_status(const unsigned char destination[6]) {
unsigned char frame[14 + 2 + 1 + 1 + own_nick_length];
write_headers(frame, destination, EMT_STATUS);
write_headers(frame, destination, 0, EMT_STATUS);
// Status
frame[16] = own_status;
@ -329,7 +333,7 @@ void send_status(const unsigned char destination[6]) {
void send_msgid_request(const unsigned char destination[6]) {
unsigned char frame[14 + 2];
write_headers(frame, destination, EMT_MSGID_REQUEST);
write_headers(frame, destination, 0, EMT_MSGID_REQUEST);
send_frame(frame, sizeof(frame));
}
@ -337,7 +341,7 @@ void send_msgid_request(const unsigned char destination[6]) {
void send_msgid(const unsigned char destination[6]) {
unsigned char frame[14 + 2 + 2];
write_headers(frame, destination, EMT_MSGID);
write_headers(frame, destination, 0, EMT_MSGID);
// Look up destination in the ID cache
ssize_t cache_index = msgid_cache_lookup(destination);
@ -366,7 +370,7 @@ void send_msgid(const unsigned char destination[6]) {
void send_message(void) {
unsigned char frame[14 + 2 + 2 + 2 + own_message_length];
write_headers(frame, own_message_destination_mac, EMT_MESSAGE);
write_headers(frame, own_message_destination_mac, 0, EMT_MESSAGE);
// Message ID
frame[16] = own_message_msgid >> 8;
@ -385,7 +389,7 @@ void send_message(void) {
void send_ack(const unsigned char destination[6], uint16_t msgid) {
unsigned char frame[14 + 2 + 2];
write_headers(frame, destination, EMT_ACK);
write_headers(frame, destination, 0, EMT_ACK);
// Message ID
frame[16] = msgid >> 8;
@ -394,6 +398,14 @@ void send_ack(const unsigned char destination[6], uint16_t msgid) {
send_frame(frame, sizeof(frame));
}
void send_file_offer(const unsigned char destination[6]) {
unsigned char frame[14 + 2];
write_headers(frame, destination, 1, EMT_FILE_OFFER);
send_frame(frame, sizeof(frame));
}
void readallx(int fd, unsigned char *buf, size_t length) {
size_t completed = 0;
while (completed < length) {
@ -495,6 +507,10 @@ void read_command(void) {
send_status(broadcast_mac);
} else if (cmd == 'm') {
read_message();
} else if (cmd == 'f') {
unsigned char mac[6];
readallx(0, mac, sizeof(mac));
send_file_offer(mac);
} else {
errx(1, "Frontend sent an unknown command %c", cmd);
}
@ -630,6 +646,29 @@ bool check_utf8(const unsigned char *data, size_t data_length, bool newline_tab_
return true;
}
void handle_speak_version(const unsigned char source_mac[6], const unsigned char *data, size_t data_length) {
if (data_length < 1) {
// Too short
return;
}
unsigned char version = data[0];
if (!check_padding(data, 1, data_length)) {
// Malformed padding
return;
}
// Type of event: Speak version
writeallx(1, "v", 1);
// MAC
writeallx(1, source_mac, 6);
// Version
writeallx(1, &version, 1);
}
void handle_status(const unsigned char source_mac[6], const unsigned char *data, size_t data_length) {
if (data_length < 2) {
// Too short
@ -835,10 +874,13 @@ void process_frame(void) {
return;
}
bool broadcast;
if (memcmp(frame, own_mac, 6) == 0) {
// Targetted at us
broadcast = false;
} else if (memcmp(frame, broadcast_mac, 6) == 0) {
// Broadcast
broadcast = true;
} else {
// Does not concern us
return;
@ -852,43 +894,58 @@ void process_frame(void) {
unsigned char version = frame[14];
// If they speak a version we don't understand, tell them to speak ours
// Exception: If it was a broadcast, just ingore it
if (version > EM_PROTOCOL_VERSION) {
send_speak_version(source_mac);
if (!broadcast) {
send_speak_version(source_mac);
}
return;
}
// Extract Ethermess packet type
unsigned char packet_type = frame[15];
// Process the packet based on the packet type
switch (packet_type) {
case EMT_STATUS_REQUEST:
if (check_padding(&frame[16], 0, packet_length - 16)) {
send_status(source_mac);
}
break;
// Process the packet based on the version and packet type
if (version == 0) {
switch (packet_type) {
case EMT_SPEAK_VERSION:
handle_speak_version(source_mac, &frame[16], packet_length - 16);
break;
case EMT_STATUS:
handle_status(source_mac, &frame[16], packet_length - 16);
break;
case EMT_STATUS_REQUEST:
if (check_padding(&frame[16], 0, packet_length - 16)) {
send_status(source_mac);
}
break;
case EMT_MSGID_REQUEST:
if (check_padding(&frame[16], 0, packet_length - 16)) {
send_msgid(source_mac);
}
break;
case EMT_STATUS:
handle_status(source_mac, &frame[16], packet_length - 16);
break;
case EMT_MSGID:
handle_msgid(source_mac, &frame[16], packet_length - 16);
break;
case EMT_MSGID_REQUEST:
if (check_padding(&frame[16], 0, packet_length - 16)) {
send_msgid(source_mac);
}
break;
case EMT_MESSAGE:
handle_message(source_mac, &frame[16], packet_length - 16);
break;
case EMT_MSGID:
handle_msgid(source_mac, &frame[16], packet_length - 16);
break;
case EMT_ACK:
handle_ack(source_mac, &frame[16], packet_length - 16);
break;
case EMT_MESSAGE:
handle_message(source_mac, &frame[16], packet_length - 16);
break;
case EMT_ACK:
handle_ack(source_mac, &frame[16], packet_length - 16);
break;
}
} else if (version == 1) {
switch (packet_type) {
case EMT_FILE_OFFER:
fprintf(stderr, "File offered\n"); //debg
break;
}
}
}

View File

@ -58,6 +58,10 @@ Version 1 EtherMess protocol version
Tells the recipient to speak the given version of the protocol.
Should be generated whenever an EtherMess packet with a version one doesn't
speak is received.
.Pp
Note however that this packet shouldn't be generated if the packet was
sent to the Ethernet broadcast address
.Li ff:ff:ff:ff:ff:ff .
.It Status request (0x01)
Requests the recipient to announce its status and nick.
Upon receiving this packet, a status packet should be sent to the sender.

View File

@ -208,6 +208,9 @@ def set_status_nick(backend, status, nick):
encoded = nick.encode('utf-8')
writeall(backend, b's' + bytes([status.value, len(encoded)]) + encoded)
def send_file_offer(backend, mac):
writeall(backend, b'f' + mac)
def handle_user_command(backend, line):
global own_nick, own_status
global default_target_mac
@ -290,13 +293,17 @@ def handle_user_command(backend, line):
# Set default target of messages
default_target_mac = mac_from_name(rest)
elif command == '/sendfile':
# Send an offer to transmit the file
send_file_offer(backend, mac_from_name(rest))
elif command == '/quit':
# Quit
return 'quit'
else:
# Display usage
print('--- / <message>; /msg <target> <message>; /status [<target>]; /peers; /available; /unavailable; /nick [<nick>]; /target <target>; /quit')
print('--- / <message>; /msg <target> <message>; /status [<target>]; /peers; /available; /unavailable; /nick [<nick>]; /target <target>; /sendfile <target>; /quit')
else:
# Send message
@ -428,7 +435,14 @@ def eventloop(proc):
for fd, event in poll.poll(wait):
if fd == proc.stdout.fileno() and event & select.POLLIN:
event_type = readall(proc.stdout, 1)
if event_type == b's':
if event_type == b'v':
# Speak version
source_mac = readall(proc.stdout, 6)
version, = readall(proc.stdout, 1)
print('--- Speak version %s %i' % (format_mac(source_mac), version)) #debg
elif event_type == b's':
# Status
source_mac = readall(proc.stdout, 6)
status, = readall(proc.stdout, 1)

View File

@ -70,6 +70,12 @@ Events
Frontend receives events from the backend.
### Speak version
Format: 'v' + MAC + version (u8)
Generated when an EtherMess Speak version packet is received by the
backend.
### Status
Format: 's' + MAC + status + nick length in bytes (u8) + nick

12
test.sh
View File

@ -1,4 +1,12 @@
#!/bin/sh
LIBEXECDIR=. make &&
test -n "$(getcap ethermess-backend)" || sudo setcap CAP_NET_RAW=ep ethermess-backend &&
LIBEXECDIR=. CFLAGS=-Werror make || exit 1
test -n "$(getcap ethermess-backend)" || sudo setcap CAP_NET_RAW=ep ethermess-backend || exit 1
if ip link show veth0a > /dev/null 2>&1 || ip link show veth0b > /dev/null 2>&1
then
true
else
sudo ip link add veth0a type veth peer name veth0b || exit 1
sudo ip link set veth0a up || exit 1
sudo ip link set veth0b up || exit 1
fi
./ethermess "$@"