Everything here assumes Linux; YMMV for other systems.
Connecting to a link local address requires setting the scope in the address structure (sin6_scope_id
in struct sockaddr_in6
). Valid scope values are interface indices, obtained by the SIOCGIFINDEX
ioctl on an open socket. Here is a small C program to print out the interface index of its first argument (save it as ifix.c
):
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
int
main(int argc, char *argv[])
{
int s;
struct ifreq ifr;
if (argc < 2) {
fprintf(stderr, "Usage: ifix <ifname>\n");
return 1;
}
s = socket(AF_UNIX, SOCK_DGRAM, 0);
if (s < 0) {
perror("ifix: socket");
return 1;
}
ifr.ifr_name[IFNAMSIZ - 1] = '\0';
strncpy(ifr.ifr_name, argv[1], IFNAMSIZ - 1);
if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
perror("ifix: SIOCGIFINDEX");
return 1;
}
printf("%d\n", ifr.ifr_ifindex);
return 0;
}
Knowing the interface index, you can use it to initialize a SocketAddrV6
struct in Rust and pass it to TcpStream::connect()
:
use std::error::Error;
use std::net::{SocketAddrV6, TcpStream};
fn main() -> Result<(), Box<dyn Error>> {
// assume: lo = 1, eth0 = 2, eth1 = 3
let sock = SocketAddrV6::new("fe80::aecc:8eff:fee2:65de".parse()?, 8080, 0, 3);
let _strm = TcpStream::connect(sock)?;
Ok(())
}
Retrieving the interface index in Rust will require a couple of raw libc
calls.
@kornel Trying to connect to a link local address without specifying the outgoing interface, by setting the scope or binding the socket to that interface, will result in an error ("invalid argument" or similar) since the kernel doesn't know where to send the packets.