Skip to content

Commit

Permalink
Merge pull request #45 from boschglobal/fix/commandline_params
Browse files Browse the repository at this point in the history
Fixed commandline parameters for the acf-can-* examples.
  • Loading branch information
adriaan-niess authored Nov 20, 2024
2 parents 89e6ced + a02cffc commit 34dba01
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 147 deletions.
79 changes: 44 additions & 35 deletions examples/acf-can/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,77 +3,86 @@
The two applications available in this folder are acf-can-listener and acf-can-talker. These applications can be used along with Linux CAN utilities. On Ubuntu/Debian Linux distributions, these utilities can be installed using the package manager `apt install can-utils`

## acf-can-talker
_acf-can-talker_ receives frames on a (virtual) CAN interface and send out the corresponding IEEE 1722 ACF messages. This application also supports a UDP encapsulation for the IEEE 1722 messages. The parameters for its usage are as follows:
_acf-can-talker_ receives frames on a (virtual) CAN interface and sends out the corresponding IEEE 1722 ACF messages. This application also supports UDP encapsulation for the IEEE 1722 messages. The parameters for its usage are as follows:

```
Usage: acf-can-talker [OPTION...]
[ifname] dst-mac-address/dst-nw-address:port [can ifname]
acf-can-talker -- a program designed to send CAN messages to
a remote CAN bus over Ethernet using Open1722
acf-can-talker -- a program designed to send CAN messages to a remote CAN bus
over Ethernet using Open1722.
--canif CAN interface
-c, --count=COUNT Set count of CAN messages per Ethernet frame
-d, --dst-addr=MACADDR Stream destination MAC address (If Ethernet)
--fd Use CAN-FD
-i, --ifname Network interface (If Ethernet)
-n, --dst-nw-addr Stream destination network address and port (If
UDP)
-t, --tscf Use TSCF
-u, --udp Use UDP
can ifname CAN interface (set to STDIN by default)
dst-mac-address Stream destination MAC address (If Ethernet)
dst-nw-address:port Stream destination network address and port (If
UDP)
ifname Network interface (If Ethernet)
```

Output of _candump_ can be piped into this application for an easy and quick use. E.g.,
```
$ candump can1 | acf-can-talker -u 127.0.0.1:17220
-?, --help Give this help list
--usage Give a short usage message
```

## acf-can-talker
_acf-can-listener_ receives IEEE 1722 ACF messages and puts out the corresponding CAN frames on a (virtual) CAN interface. Analogous to the _acf_can_talker_, UDP encapsulation is also available for this application. The parameters for its usage are as follows:

```
Usage: acf-can-listener [OPTION...] [ifname] dst-mac-address [can ifname]
Usage: acf-can-listener [OPTION...]
acf-can-listener -- a program designed to receive CAN messages from
a remote CAN bus over Ethernet using Open1722
acf-can-listener -- a program designed to receive CAN messages from a remote
CAN bus over Ethernet using Open1722.
-p, --port=UDP_PORT UDP Port to listen on if UDP enabled
--canif CAN interface
-d, --dst-addr=MACADDR Stream destination MAC address (If Ethernet)
--fd Use CAN-FD
-i, --ifname Network interface (If Ethernet)
-p, --udp-port UDP Port to listen on (if UDP)
-u, --udp Use UDP
can ifname CAN interface (set to STDOUT by default)
dst-mac-address Stream destination MAC address (If Ethernet)
ifname Network interface (If Ethernet)
-?, --help Give this help list
--usage Give a short usage message
```

Output of this application can also be piped to _canplayer_ if so desired. E.g.,
```
acf-can-listener -up 17220 | canplayer can1=elmcan can1
```

## Quickstart Tutorial: Tunneling CAN over IEEE 1722 using Linux CAN utilities
Here is an example of how CAN frames can be tunneled over an Ethernet link using _acf-can-talker_ and _acf-can-listener_.
We use two virtual CAN interfaces, vcan0 and vcan1, here which can be setup using following commands:
We use two virtual CAN interfaces, _vcan0_ and _vcan1_, which can be setup using following commands:
```
$ ip link add dev vcan0 type vcan # Execute these commands also for vcan1
$ modprobe vcan
$ ip link add dev vcan0 type vcan # Execute these commands also for vcan1
$ ip link set dev vcan0 up
```

### Generate CAN traffic
On Terminal 1, generate CAN traffic for vcan0:
```
$ cangen vcan0
```

On Terminal 2, pipe generated CAN traffic to _acf-can-talker_. Here, we use UDP encapsulation:
In the following, we tunnel this generated CAN traffic over Ethernet to _vcan1_

### Use Talker Application for tunneling
On Terminal 2, pipe generated CAN traffic from vcan0 to _acf-can-talker_. Here, we use UDP encapsulation.
```
$ candump vcan0 | acf-can-talker -u 127.0.0.1:17220
$ ./acf-can-talker -u --dst-nw-addr 127.0.0.1:17220 --canif vcan0
```

On Terminal 3, receive the IEEE 1722 traffic using _acf-can-listener_ and pipe the output to _canplayer_ for putting the CAN frame out on vcan1.
Alternatively, we can directly use Ethernet for transport.
```
$ acf-can-listener -up 17220 | canplayer vcan1=elmcan
$ ./acf-can-talker --dst-addr aa:bb:cc:dd:ee:ff -i eth0 --canif vcan0
```

You can now compare CAN traffic seen on vcan0 and vcan1, if the tunneling has worked.
Note that the tunneling works only in one direction (vcan0 -> vcan1).
### Use Listener Application for receiving
On Terminal 3, receive the IEEE 1722 traffic using _acf-can-listener_ and put the CAN frame out on vcan1.

If the talker uses UDP encapsulation:
```
$ ./acf-can-listener -u -p 17220 --canif vcan1
```

Alternatively, if Ethernet is directly used:
```
$ ./acf-can-listener --dst-addr aa:bb:cc:dd:ee:ff -i eth0 --canif vcan0
```

You can now compare CAN traffic seen on _vcan0 and vcan1_ to check if the tunneling works.
Note that the tunneling works in these examples only in one direction (_vcan0_ -> _vcan1_).
77 changes: 26 additions & 51 deletions examples/acf-can/acf-can-listener.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@

#define MAX_PDU_SIZE 1500
#define ARGPARSE_CAN_FD_OPTION 500
#define ARGPARSE_CAN_IF_OPTION 501

static char ifname[IFNAMSIZ];
static uint8_t macaddr[ETH_ALEN];
Expand All @@ -60,25 +61,21 @@ static uint32_t udp_port = 17220;
static Avtp_CanVariant_t can_variant = AVTP_CAN_CLASSIC;
static char can_ifname[IFNAMSIZ];

static char doc[] = "\nacf-can-listener -- a program designed to receive CAN messages from \
a remote CAN bus over Ethernet using Open1722 \
\vEXAMPLES\
\n\n acf-can-listener eth0 aa:bb:cc:dd:ee:ff can1\
\n\n (tunnel Open1722 CAN messages received from eth0 to STDOUT)\
\n\n acf-can-listener can1 -up 1722\
\n\n (tunnel Open1722 CAN messages received over UDP from port 1722 to can1)\
\n\n acf-can-listener -up 1722 | canplayer can1=elmcan\
\n\n (another method to tunnel Open1722 CAN messages to can1)";

static char args_doc[] = "[ifname] dst-mac-address [can ifname]";
static char doc[] =
"\nacf-can-listener -- a program designed to receive CAN messages from a remote CAN bus over Ethernet using Open1722.\
\vEXAMPLES\n\
acf-can-listener -i eth0 -d aa:bb:cc:dd:ee:ff --canif can1\n\
\t(tunnel Open1722 CAN messages received from eth0 to can1)\n\
acf-can-listener --canif can1 -u -p 17220\n\
\t(tunnel Open1722 CAN messages received over UDP from port 17220 to can1)";

static struct argp_option options[] = {
{"port", 'p', "UDP_PORT", 0, "UDP Port to listen on if UDP enabled"},
{"udp", 'u', 0, 0, "Use UDP"},
{"udp", 'u', 0, 0, "Use UDP" },
{"fd", ARGPARSE_CAN_FD_OPTION, 0, 0, "Use CAN-FD"},
{"can ifname", 0, 0, OPTION_DOC, "CAN interface (set to STDOUT by default)"},
{"dst-mac-address", 0, 0, OPTION_DOC, "Stream destination MAC address (If Ethernet)"},
{"ifname", 0, 0, OPTION_DOC, "Network interface (If Ethernet)" },
{"canif", ARGPARSE_CAN_IF_OPTION, "CAN_IF", 0, "CAN interface"},
{"ifname", 'i', "IFNAME", 0, "Network interface (If Ethernet)"},
{"dst-addr", 'd', "MACADDR", 0, "Stream destination MAC address (If Ethernet)"},
{"udp-port", 'p', "UDP_PORT", 0, "UDP Port to listen on (if UDP)"},
{ 0 }
};

Expand All @@ -95,49 +92,27 @@ static error_t parser(int key, char *arg, struct argp_state *state)
break;
case ARGPARSE_CAN_FD_OPTION:
can_variant = AVTP_CAN_FD;

case ARGP_KEY_NO_ARGS:
case ARGPARSE_CAN_IF_OPTION:
strncpy(can_ifname, arg, sizeof(can_ifname) - 1);
break;

case ARGP_KEY_ARG:

if(state->argc < 2){
argp_usage(state);
}

if(!use_udp){
strncpy(ifname, arg, sizeof(ifname) - 1);

if(state->next < state->argc)
{
res = sscanf(state->argv[state->next], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&macaddr[0], &macaddr[1], &macaddr[2],
&macaddr[3], &macaddr[4], &macaddr[5]);
if (res != 6) {
fprintf(stderr, "Invalid MAC address\n\n");
argp_usage(state);
}
state->next += 1;
}

if(state->next < state->argc)
{
strncpy(can_ifname, state->argv[state->next], sizeof(can_ifname) - 1);
state->next = state->argc;
}

}else{
strncpy(can_ifname, arg, sizeof(can_ifname) - 1);
state->next = state->argc;
case 'i':
strncpy(ifname, arg, sizeof(ifname) - 1);
break;
case 'd':
res = sscanf(arg, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&macaddr[0], &macaddr[1], &macaddr[2],
&macaddr[3], &macaddr[4], &macaddr[5]);
if (res != 6) {
fprintf(stderr, "Invalid MAC address\n");
exit(EXIT_FAILURE);
}

break;
}

return 0;
}

static struct argp argp = { options, parser, args_doc, doc };
static struct argp argp = { options, parser, NULL, doc};

static int is_valid_acf_packet(uint8_t* acf_pdu)
{
Expand Down
98 changes: 37 additions & 61 deletions examples/acf-can/acf-can-talker.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
#define STREAM_ID 0xAABBCCDDEEFF0001
#define CAN_PAYLOAD_MAX_SIZE 16*4
#define ARGPARSE_CAN_FD_OPTION 500
#define ARGPARSE_CAN_IF_OPTION 501

static char ifname[IFNAMSIZ];
static uint8_t macaddr[ETH_ALEN];
Expand All @@ -66,29 +67,23 @@ static Avtp_CanVariant_t can_variant = AVTP_CAN_CLASSIC;
static uint8_t num_acf_msgs = 1;
static char can_ifname[IFNAMSIZ];

static char doc[] = "\nacf-can-talker -- a program designed to send CAN messages to \
a remote CAN bus over Ethernet using Open1722 \
\vEXAMPLES\
\n\n acf-can-talker eth0 aa:bb:cc:ee:dd:ff\
\n\n (tunnel transactions from STDIN to a remote CAN bus over Ethernet)\
\n\n acf-can-talker --count 10 eth0 aa:bb:cc:ee:dd:ff\
\n\n (as above, but pack 10 CAN frames in one Ethernet frame)\
\n\n acf-can-talker -u 10.0.0.2:17220 vcan1\
\n\n (tunnel transactions from can1 interface to a remote CAN bus over IP)\
\n\n candump can1 | acf-can-talker -u 10.0.0.2:17220\
\n\n (another method to tunnel transactions from vcan1 to a remote CAN bus)";

static char args_doc[] = "[ifname] dst-mac-address/dst-nw-address:port [can ifname]";
static char doc[] =
"\nacf-can-talker -- a program designed to send CAN messages to a remote CAN bus over Ethernet using Open1722.\
\vEXAMPLES\n\
acf-can-talker -i eth0 -d aa:bb:cc:ee:dd:ff --canif vcan0\n\
\t(tunnel transactions from CAN vcan0 over Ethernet eth0)\n\n\
acf-can-talker -u --dst-nw-addr 10.0.0.2:17220 --canif vcan1\n\
\t(tunnel transactions from vcan1 interface using UDP)";

static struct argp_option options[] = {
{"tscf", 't', 0, 0, "Use TSCF"},
{"udp", 'u', 0, 0, "Use UDP" },
{"fd", ARGPARSE_CAN_FD_OPTION, 0, 0, "Use CAN-FD"},
{"count", 'c', "COUNT", 0, "Set count of CAN messages per Ethernet frame"},
{"can ifname", 0, 0, OPTION_DOC, "CAN interface (set to STDIN by default)"},
{"ifname", 0, 0, OPTION_DOC, "Network interface (If Ethernet)"},
{"dst-mac-address", 0, 0, OPTION_DOC, "Stream destination MAC address (If Ethernet)"},
{"dst-nw-address:port", 0, 0, OPTION_DOC, "Stream destination network address and port (If UDP)"},
{"canif", ARGPARSE_CAN_IF_OPTION, "CAN_IF", 0, "CAN interface"},
{"ifname", 'i', "IFNAME", 0, "Network interface (If Ethernet)"},
{"dst-addr", 'd', "MACADDR", 0, "Stream destination MAC address (If Ethernet)"},
{"dst-nw-addr", 'n', "NW_ADDR", 0, "Stream destination network address and port (If UDP)"},
{ 0 }
};

Expand All @@ -110,58 +105,39 @@ static error_t parser(int key, char *arg, struct argp_state *state)
case ARGPARSE_CAN_FD_OPTION:
can_variant = AVTP_CAN_FD;
break;

case ARGP_KEY_NO_ARGS:
argp_usage(state);

case ARGP_KEY_ARG:

if(state->argc < 2){
argp_usage(state);
case ARGPARSE_CAN_IF_OPTION:
strncpy(can_ifname, arg, sizeof(can_ifname) - 1);
break;
case 'i':
strncpy(ifname, arg, sizeof(ifname) - 1);
break;
case 'd':
res = sscanf(arg, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&macaddr[0], &macaddr[1], &macaddr[2],
&macaddr[3], &macaddr[4], &macaddr[5]);
if (res != 6) {
fprintf(stderr, "Invalid MAC address\n");
exit(EXIT_FAILURE);
}

if(!use_udp){

strncpy(ifname, arg, sizeof(ifname) - 1);

if(state->next < state->argc)
{
res = sscanf(state->argv[state->next], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
&macaddr[0], &macaddr[1], &macaddr[2],
&macaddr[3], &macaddr[4], &macaddr[5]);
if (res != 6) {
fprintf(stderr, "Invalid MAC address\n\n");
argp_usage(state);
}
state->next += 1;
}

} else {
res = sscanf(arg, "%[^:]:%d", ip_addr_str, &udp_port);
if (!res) {
fprintf(stderr, "Invalid IP address or port\n\n");
argp_usage(state);
}
res = inet_pton(AF_INET, ip_addr_str, ip_addr);
if (!res) {
fprintf(stderr, "Invalid IP address\n\n");
argp_usage(state);
}
break;
case 'n':
res = sscanf(arg, "%[^:]:%d", ip_addr_str, &udp_port);
if (!res) {
fprintf(stderr, "Invalid IP address or port\n");
exit(EXIT_FAILURE);
}

if(state->next < state->argc)
{
strncpy(can_ifname, state->argv[state->next], sizeof(can_ifname) - 1);
state->next = state->argc;
res = inet_pton(AF_INET, ip_addr_str, ip_addr);
if (!res) {
fprintf(stderr, "Invalid IP address\n");
exit(EXIT_FAILURE);
}

break;
}

return 0;
}

static struct argp argp = { options, parser, args_doc, doc };
static struct argp argp = { options, parser, NULL, doc};

static int init_cf_pdu(uint8_t* pdu)
{
Expand Down Expand Up @@ -247,7 +223,7 @@ int main(int argc, char *argv[])
uint16_t pdu_length, cf_length;
frame_t can_frame;

argp_parse(&argp, argc, argv, 0, NULL, NULL);
argp_parse(&argp, argc, argv, doc, NULL, NULL);

// Create an appropriate talker socket: UDP or Ethernet raw
// Setup the socket for sending to the destination
Expand Down

0 comments on commit 34dba01

Please sign in to comment.