I'm trying to send data via UDP to the network. I've got some PHP code running on my local machine which works:
#!/usr/bin/php -q
<?php
$socket = stream_socket_client('udp://225.0.0.0:50000');
for($i=0;$i<strlen($argv[1]);$i++) $b.="\0\0\0".$argv[1][$i];
fwrite($socket,$b,strlen($argv[1])*4);
fclose($socket);
?>
Gives me the output in tcpdump:
18:53:24.504447 IP 10.0.1.2.52919 > 225.0.0.0.50000: UDP, length 36
I'm trying to get to the same result on a remote iOS with the following code:
- (void)broadcast:(NSString *)dx {
NSData* data=[dx dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"Broadcasting data: %#", dx);
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in addr4client;
memset(&addr4client, 0, sizeof(addr4client));
addr4client.sin_len = sizeof(addr4client);
addr4client.sin_family = AF_INET;
addr4client.sin_port = htons(PORT);
addr4client.sin_addr.s_addr = htonl(INADDR_BROADCAST);
int yes = 1;
if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (void *)&yes, sizeof(yes)) == -1) {
NSLog([NSString stringWithFormat:#"Failure to set broadcast! : %d", errno]);
}
char *toSend = (char *)[data bytes];
if (sendto(fd, toSend, [data length], 0, (struct sockaddr *)&addr4client, sizeof(addr4client)) == -1) {
NSLog([NSString stringWithFormat:#"Failure to send! : %d", errno]);
}
close(fd);
}
Which gives me the following output in tcpdump:
19:01:22.776192 IP 10.0.1.4.60643 > broadcasthost.50000: UDP, length 9
Looks basically OK, but doesn't arrive in Quartz Composer for some reason, I guess there should be the IP address or something instead of 'broadcasthost'.
Any idea?
The problem was not in the implementation of the broadcaster, but the format of the string. To work with Quartz Composer, every character needs to be preceded by a backslash-zero combination: "\0\0\0", so "abc" has to be formatted and sent as "\0\0\0a\0\0\0b\0\0\0c".
See also Celso Martinho's blog article: Leopard’s Quartz Composer and Network events.
I suggest using AsyncSocket ( google it, its on googlecode ), very well tested objective-c code that runs on iOS.
That way you can send data really easy using a NSData object. AsyncSocket manages the hard part for you.
If that isn't an option for you you should use CFSocket. What you are doing is implementing code that has been written for you already, CFSocket.
Related
I have un issue with calculating the checksum for IPv6 packet in the linux kernel module.
I tried the next way:
struct in6_addr LINK_LOCAL_MULTICAST = {{{ 0xff,02,0,0,0,0,0,0,0,0,0,0,0,1,0,2 }}};
struct in6_addr LINK_LOCAL_SRC = {{{ 0xfe,0x80,0,0,0,0,0,0,0x0a,0x00,0x27,0xff,0xfe,0x5b,0x58,0xcf }}};
udph->len = htons(sizeof(struct udphdr)+sizeof(struct udp_payload));
__wsum csum = csum_partial((char*) udph, udhp->len, 0);
udph->check = csum_ipv6_magic(&LINK_LOCAL_SRC, &LINK_LOCAL_MULTICAST, udph->len, IPPROTO_UDP,csum);
But the checksum seems to be incorrect. Could you please suggest what I have to change to get correct checksum.
EDIT1:
Please find the packet in wireshark. I changed the offload settings(tx,rx) but the checksum still incorrect. I am afraid that the value in the checksum is wrong.
I am unable to set TUN interface. Everywhere i searched and it says the device should be rooted.
I am setting up proxyserver on my ubuntu 14.04 system
static int get_interface(char *name) {
int interface = open("/dev/net/tun", O_RDWR | O_NONBLOCK);
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
if (ioctl(interface, TUNSETIFF, (void *)&ifr) < 0) {
perror("Cannot get TUN interface");
exit(1);
}
return interface;
}
Check your device name (i.e. ifr.ifr_name). Another process maybe using the same device. For example, you may be trying to use tun0 and another process has it open already.
I have a fairly basic user space firewall application. It receives data from libnetfilter_queue properly, we can see all the IP and TCP header information including source and destination IPs, ports, protocols, etc, but we don't get ANY of the TCP payload information...
The setup is pretty standard, I won't include it all but here are the highlights:
#define MAX_BUFFER_SIZE 65535
nfq_set_mode(qh, NFQNL_COPY_PACKET, MAX_BUFFER_SIZE)
So we are asking for the FULL PACKET back...
In the main thread we have:
rv = recv(fd, nfq_buffer, sizeof(nfq_buffer), 0);
printf("\nGot packet len: %d", rv);
if (rv > 0)
nfq_handle_packet(nfq_h, (char*)nfq_buffer, rv);
Here, on a standard HTTP call I will get a packet length of 140. But, in the callback handler the PACKET length is ALWAYS 64:
static int handle_packet(struct nfq_q_handle* qh, struct nfgenmsg* nfmsg, struct nfq_data* dat, void* data)
{
struct nfqnl_msg_packet_hdr* nfq_hdr = nfq_get_msg_packet_hdr(dat);
unsigned char* nf_packet;
int len = nfq_get_payload(dat,&nf_packet);
struct iphdr *iph = ((struct iphdr *) nf_packet);
iphdr_size = iph->ihl << 2;
if (iph->protocol == 6){
struct tcphdr *tcp = ((struct tcphdr *) (nf_packet + iphdr_size));
unsigned short tcphdr_size = (tcp->doff << 2);
printf("\nGot a packet!! len: %d iphdr: %d tcphdr: %d", len, iphdr_size, tcphdr_size);
}
}
In TCP, len is ALWAYS 64 (iphdr is 20, tcphdr is 44)... ALWAYS. I never get the TCP payload. What am I doing wrong???
Thanks to Joel for pointing out that problem was not in the C code, but in the iptables rules. There was a -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT rule prior to the NFQUEUE rule, so I was only getting the connection setup packets... whoops.
I was wondering if there is a way to copy a packet using iptables/netfilter, change it and deliver both to the application.
Basically, I want to capture a packet from a flow and redirect it to some queue, then I want to copy it, issue the verdict for it(I know how to do this part in C),then I need to change something in the copied version, AND issue the verdict for that "modified" packet too.
Basically I want the app to receive both the unmodified and the modified version.
Is this possible?
Thanks in advance for any help.
Your mission can be achieved with libipq library. The tutorial in following like focus on copying & modifying a packet in userspace.
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.205.2605&rep=rep1&type=pdf
You need to know C to work on it. Alternatively "Scapy" - a python based packet maipulation tool can be used.
#include <linux/netfilter.h>
#include <libipq.h>
/*
* Used to open packet ; Insert a iptables rule to get packet here
* iptables -I 1 [INPUT|OUTPUT|FORWARD] <packet header match> -j QUEUE
*/
#include <linux/netfilter.h>
#include <libipq.h>
#include <stdio.h>
#define BUFSIZE 2048
static void die(struct ipq_handle *h)
{
ipq_destroy_handle(h);
exit(1);
}
int main(int argc, char **argv)
{
int status;
unsigned char buf[BUFSIZE];
struct ipq_handle *h;
h = ipq_create_handle(0, NFPROTO_IPV4);
if (!h)
die(h);
status = ipq_set_mode(h, IPQ_COPY_PACKET, BUFSIZE);
if (status < 0)
die(h);
do{
status = ipq_read(h, buf, BUFSIZE, 0);
if (status < 0)
die(h);
if (ipq_message_type(buf) == IPQM_PACKET){
ipq_packet_msg_t *m = ipq_get_packet(buf);
status = ipq_set_verdict(h, m->packet_id, NF_ACCEPT, 0, NULL);
}
} while (1);
ipq_destroy_handle(h);
return 0;
}
see the following code:
accept(sockfd, (struct sockaddr*)&cliaddr, &slen);
cout << inet_ntop(AF_INET, cliaddr.sin_addr, ipv4addr, 100);
my client connects from localhost.
i get an absurd address in the output. this is not my ip address. everytime i run the code i get a different ip address. when i ping that ip address i don't get any response.
what is the reason.
i am running suse linux on a virtual machine in windows vista.
Update:
bzero(&cliaddr, sizeof(cliaddr));
int connfd = accept(sockfd, (struct sockaddr*)&cliaddr, &slen);
if (sem_wait(&mutex) < 0)
err_sys("sem_init error");
char ipv4addr[100];
cout << inet_ntop(AF_INET, &cliaddr.sin_addr, ipv4addr, 100) << endl;
//const char* p = inet_ntop(AF_INET, &cliaddr.sin_addr, ipv4addr, 100);
//cout << p << endl;
//cout << (void*)p << " " << (void*)ipv4addr << endl;
this returns address as 0.0.0.0
if i uncomment the lines, i get the correct address in all the lines, 127.0.0.1
You are missing the 4th parameter in your call to inet_ntop(). Here's a working example:
int sockfd, fd;
struct sockaddr_in saddr;
socklen_t len = sizeof( saddr );
char addr_buf[INET_ADDRSTRLEN]; /* defined in <netinet/in.h> */
/* ... socket(), bind(), listen() */
bzero( &saddr, len );
if (( fd = accept( sockfd, ( struct sockaddr* )&saddr, &len )) == -1 )
{ perror( "accept" ); exit( 1 ); } /* watch out for EINTR */
if ( inet_ntop( AF_INET, &saddr.sin_addr, addr_buf,
INET_ADDRSTRLEN ) == NULL )
{ perror( "inet_ntop" ); exit( 1 ); }
printf( "accepted connection from [%s:%d]\n",
addr_buf, ntohs( saddr.sin_port ));
...
Always check for errors when talking to network.
My unsubstantiated guess is that you're getting IP v6 addresses back instead of v4, so your conversion is off.
You might want to try using netstat to find out the client's port (you usually get a sort-of-random port number between 1025 and 65535) and see if the hex value of that appears somewhere in the hex representation of cliaddr. If there's a correlation between client port and what you believe to be the client address, then your conversion is incorrect.
My next guess:
On success, inet_ntop() returns a non-null pointer to dst. NULL is
returned if there was an error, with errno set to indicate the error.
Is cout.<< clever enough to dereference the pointer that's being returned, or are you printing out the pointer?