IPv6 multicast with Node.js - node.js

I am experimenting with IPv6 UDP multicast over a VPN. I have tried the following code:
const dgram = require('dgram');
let sock = dgram.createSocket('udp6', {
reuseAddr: true
});
sock.on('message', (data, source) => {
console.log('on message', arguments);
});
sock.bind('36912', '2620:9b::1944:e598', () => {
sock.addMembership('ff02::1:3', '2620:9b::1944:e598');
});
setInterval(() => {
let buf = Buffer.from((new Date()).toString());
sock.send(buf, 0, buf.length, 36912, 'ff02::1:3');
}, 500);
The script runs, and I see packets being sent/received with Wireshark, but neither end shows them in the console.
What am I doing wrong? What's a way to send and receive basic multicast with IPv6?

Scope id -> interface number
In IPv6, there is a concept of a scope_id of an address that is supposed to indicate a context for the IP address, and generally just means what interface it is reachable on. While scopes have OS specific names, each just translates to an interface number, with 0 usually meaning the system's default.
In IPv6 multicast, this scope_id is provided directly to IP_ADD_MEMBERSHIP and IP_MULTICAST_IF instead of providing an ip associated with the interface as IPv4 does.
wrapper smoothing of v6's differences
node (via libuv) hides this difference for you in addMembership by looking up the scope_id from the "interface address" you provide.
Unfortunately, starting from just an IP and getting a scope doesn't make a lot of sense (the whole point of the scope is that the IP could have different uses in different scopes.) So libuv is only able to fill in a scope if you explicitly provide it at the end of the address, using the %[scope] format.
Using Addresses with Explicit Scopes
The way around this seems to be:
sock.bind('36912', '::', () => {
sock.addMembership('ff02::1:3', '::%eth2');
...
sock.send(buf, 0, buf.length, 36912, 'ff02::1:3%eth2');
Where:
using :: (or no address) in bind is necessary since you are combining receive which will filter the multicast address on this with send which needs a normal address.
using %[iface#] forces the scope of this interface #.
The second argument of addMembership could really start with any address since we are forcing the scope and the rest is discarded.
Usually the send socket is separated and given a different port or an anonymous port as you are either limited in what you can configure or in danger of getting EADDRINUSE errors for having sockets that are too similar.

I followed the steps in this answer but still couldn't get IPv6 multicast to work. It turned out I was setting socket.setMulticastLoopback(false) to filter out messages coming from the node itself, which worked well for IPv4, but was blocking all messages for IPv6. Removing this fixed the issue and messages started appearing correctly, with no need to filter.

Related

Is it possible to achieve a p2p connection without an external server if the public IP and listening port are known?

For context, my group and I are attempting to build a simple p2p messaging application in Rust with minimal library use. (We DID attempt using libp2p early on, but unfortunately it's incompatible with the key exchange algorithm we're using.)
The users on each end are required to send one another a public key through a third party messaging service before connecting, and we are able to encode the public IP address and the listening port of the program within this public key. (Meaning that the public IP and listening port of the other party will be known by the program at runtime.)
Since we are able to communicate the router's public IP address and the listening port of the program, would it be possible to establish a p2p connection without the need for an external server or port forwarding? If so, is there a simple solution we're not seeing using only the standard library? Currently we're attempting to check for incoming connections using TcpListener (see test code below) and are able to detect connections to localhost on the specified port, but have no access over the network.
We're all college students who are new to networking, so any explanation for what technology we're looking for would be greatly appreciated. We've tried researching hole punching, but to our understanding that requires a third server with a known open port. We were hoping that by broadcasting the IP and listening port directly we could bypass this. We're operating on the school's network, so UPnP is disabled.
use std::net::{TcpListener, TcpStream};
// Simple test script for TcpListener - attempting to listen over the network instead of locally.
// Handle new connections - test program just prints info and allows the connection to close.
fn handle_connection(stream: TcpStream) {
println!("New Client: {}", stream.peer_addr().unwrap());
}
fn main() -> std::io::Result<()> {
// Listen on any IP and let the OS choose a port - works with localhost and local address
// shown from "ipconfig". Does not work with public IP shown in web browsers.
// (This is expected - no open port or specialized incoming communication yet.)
let listener = TcpListener::bind("0.0.0.0:0").unwrap();
// Show listening port for testing purposes.
println!("Listening on: {}", listener.local_addr().unwrap());
// Attempt to connect to all incoming streams.
for stream in listener.incoming() {
match stream {
Ok(stream) => {
handle_connection(stream);
}
Err(_) => {
eprintln!("Connection Error");
}
}
}
Ok(())
}

erlang bind http query to ip (or interface)

In nodejs we have http.request(options[, callback]) with option localAddress ( Local interface to bind for network connections.). This is working pretty good. How can I implement this in Erlang? Now I use ibrowse for requests but this is not a constraint. I think I need to see how plain http in erlang works, but may be someone faced with this.
Basic TCP in Erlang provides an option for gen_tcp:connect/3,4 where you can declare a specific interface (or socket type, and a few other things).
If I only want to connect to some remote Host:Port the call looks like:
{ok, Socket} = gen_tcp:connect(Host, Port, [])
If I want the local side of the connection to originate from a specific address (on this machine 192.168.5.23 is wlan0, 192.168.7.67 is eth0, for example) I can do:
{ok, Socket} = gen_tcp:connect(Host, Port, [{ifaddr, {192,168,5,23}}])
If I want to connect from a specific port, I would add the port option:
Options = [{ifaddr, {192, 168, 5, 23}}, {port, 11311}],
{ok, Socket} = gen_tcp:connect(Host, Port, Options),
That's just vanilla TCP. Writing an HTTP/1.1 client in Erlang is pretty easy, and depending on what you want to do may be ideal.
There is also a built-in http client called httpc (and a few other somewhat more featureful/cleaner ones around like gun and hackney). These all make requests using similar arguments to gen_tcp, including availability of the same connect options as well as a special httpc:set_options/1,2 that is pretty straightforward to use:
ok = inets:start(),
ok = httpc:set_options([{ip, {192, 168, 5, 23}}, {port, 11311}]),
{ok, Response} = httpc:request("http://zxq9.com/archives/1311"),
Hopefully this is enough information to get you started.

Node.js sticky-session, what if mobile IP has been changed?

We know sticky-session uses hash of IP address to make each client connect to same cluster(or process). And instead, I use sticky-cluster which has same principle, but much better.
Then... what if IP changes in mobile?
We also know every mobile device move, its IP could be changed.
If it happens, the handshake protocol will be broken.
In case of me, Because I have to use socket.io to make messenger app, I must solve this.
Actually, I sent email to sticky-cluster developer, Denis Zhbankov,
and he said it is a drawback of sticky-session, but master should decide which worker receive connection without actually read data (headers, cookies..)..... unfortunately there was no solution... so I came here.
But I tried to find solution. I hope it will help you for hint.
function serverCreate () {
var hash = require('string-hash');
return require('net')
.createServer({ pauseOnConnect: true }, function (connection) {
var index = hash(connection.remoteAddress || '') % CONCURRENCY;
workers.entrust(index, connection);
});
}
This is code sample of sticky-cluster. As you see it uses hash of ip, get idx, and pass connection to workers[ idx ]. (Balancing)
At here, I thought there would be some unique identifier, which can replace ip, in connection (argument). So I unwraped the connection(Socket) object.
But there was nothing to replace ip...It was stupid! :D
If you have any ideas to solve this problem, please answer anything..;)
Short: How to make a client to connect same cluster even client IP changes in mobile when using sticky-session.

How to keep a tcp connection always open with node.js

I'm building a tcp-message server with nodejs.
It just sits on my server waiting to accept a connection from lets say an Arduino.
As it, at connection-time, identifies itself with an unique ID (not an IP) I'm able to write data from server > arduino without knowing the IP address of the client-device.
But for that to be efficient, I want the connection to be open as long as possible, preferably as long as the client-device closes the connection. (eg on ip change or something)
This is the (relevant part of) the server:
var net = require('net'),
sockets = {};
var tcp = net.createServer(function(soc){
soc.setKeepAlive(true); //- 1
soc.on('connect', function(data){
soc.setKeepAlive(true); //- 2
});
soc.on('data', function(data) {
//- do stuff with the data, and after identification
// store in sockets{}
})
}).listen(1111);
Is soc.setKeepAlive(true) the right method to keep the connection alive?
If so, what is the right place to put it? In the connect event (1), or right in the callback (2).
If this is not the way to do it, what is?
Your best bet is to periodically send your own heartbeat messages.
Also, you don't need soc.on('connect', ...) because the client socket is already connected when that callback is executed.

Node.js: Cannot access server from different device on the same network

NOTE: There are some others who have had similar problems, but those were solved by fixing small tidbits in the code involving how the server was listening (in examples that I've seen they put '127.0.0.1' as an argument in http.createServer(...).listen(). However, I do not have the same issue.
When I try to connect to my node.js server from a different machine on the same LAN network, Chrome says that it cannot connect.
This is testtesttest.js
var http = require('http');
http.createServer(function(req,res) {
res.writeHead(200,{'Content-Type': 'text/plain'});
res.end('Working');
}).listen(3000);
When I try inputting 192.168.1.73:3000 (of course 192.168.1.73 is the ip of the machine that I'm running the server on) into the browser (Chrome, although I've tried other browsers as well and I have similar problems) of my other machine, it gives the error "Oops! Google Chrome could not connect to 192.168.1.73:3000". When I type the same address onto the local machine, it works fine.
I'm not exactly sure what to do. I honestly hope this is just a stupid mistake on my part (I'm sorry for possibly wasting your time) and not something that I have to go into my router for.
Thanks very much for any help.
Try changing this
.listen(3000);
to this
.listen(3000, "0.0.0.0");
Just putting this here in case it saves anyone else. I had this problem for two full days when trying to connect my phone to my local machine... and it was because the wifi on my phone was turned off.
I too had this problem.
I solved it by allowing "node.js" in the network group.
Solution : Allowing "node.js" through the private network windows firewall
a. Go to Control Panel
b. Go to Windows Firewall
c. Click on the Allow an app or feature through windows firewall in the left sidebar
d. Search for "Node.js : Server Side JavaScript" and make sure both public and private column box is marked tick for "NodeJS"
e. Click "OK" and you are done
Below is the step i followed which Worked
My server code
var http=require('http');
http.createServer(function(request,response){
response.writeHead(200,{'Content-Type':'text/plain'});
response.end('Im Node.js.!\n');
console.log('Handled request');
}).listen(8080, "0.0.0.0");;
console.log('Server running a http://localhost:8080/');
Added inbound Rules.
Created a udp inbound rule(since i could'nt find any http protocol).
Once created go to properties for the created rule.
Choose Protocols and Properties tab.
Choose any in Port Type. Click apply and Ok. Now i tried from other
machine it worked!!!
I think you need to set Port Type to any to make it work.
Thanks
You have to open 3000 port so that it can be accessed from remote machines. You can do that in iptables file. Use below command to open file
vi /etc/sysconfig/iptables
now add below line before the reject lines in that file
-A INPUT -p tcp -m tcp --dport 3000 -j ACCEPT
Now restart iptables service using below command
service iptables restart
Now you restart your server and try again. It should work..
Chances are your firewall settings block incoming request on port 3000. You may want to add firewall inbound rule on this port to allow access to it.
For me, the culprit was a VirtualBox Host-only Network interface. The presence of this network interface was causing ipconfig on the server to report 192.168.56.1 instead of the router assigned address of 192.168.1.x. I was accessing the wrong IP all along.
To remove the VirtualBox Host-only Network interface:
Open VirtualBox
Go to File>Preference>Network>Host-only Networks
Remove the offending adapter(s)
My problem was that, I have used IP assigned to my ethernet adapter instead of wifi adapter...
And it now works when I connect from any device.
http.createServer(function(req,res) {
res.writeHead(200,{'Content-Type': 'text/plain'});
res.end('Working');
}).listen(3000, "192.168.1.36");
I found my IPv4 address on network settings, then specify with listen fun. put also 3000 port. I can reach http://192.168.1.36:3000/ via my tablet which is connected same wifi.
You have to run that on the terminal:
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
Explanation here
Like what Umamaheswaran answered, a new inbound rule needs to be created. But instead of using the UDP protocol, I have to use TCP. My application runs on a Windows Server 2012. The Inbound Rules are set in Windows Firewall with Advanced Security, under Administrative Tools
I solved this using this algorithm. What it does is get all ip info from os.networkInterfaces, get all keys and loop trough it, filter by objects that contains family == "IPv4" and get it address value. After, it looks for '192.168.0.xxx' pattern and put it in possible_ip array. Any other ip will be pushed to other_ips array.
The export sentence is to turn it into a module js file.
import by import {get_ip} from ./your_path/name_of_your_file.js, or in common js const { get_ip } = require("./your_path/name_of_your_file.js");
Hop it helps
const os = require('os');
const _ = os.networkInterfaces();
// if common js use
// exports.get_ip = () => {
export const get_ip = () => {
let ips = [];
Object.keys(_).map(key => {
let ipv4_obj = _[key].filter(obj => obj.family === "IPv4")
let ipv4 = ipv4_obj[0].address
ips.push(ipv4)
})
let other_ips = []
let possible_ip = []
ips.map(ip => {
ip.includes("192.168.0.") ?
possible_ip.push(ip) :
other_ips.push(ip)
})
return { possible_ip, other_ips }
}

Resources