Computer Networks Lab: 1) Write A Program For Distance Vector Algorithm To Find Suitable Path For Transmission
Computer Networks Lab: 1) Write A Program For Distance Vector Algorithm To Find Suitable Path For Transmission
Computer Networks Lab: 1) Write A Program For Distance Vector Algorithm To Find Suitable Path For Transmission
1) Write a program for distance vector algorithm to find suitable path for transmission.
Distance Vector Algorithm is a decentralized routing algorithm that requires that each
router simply inform its neighbors of its routing table. For each network path, the receiving
routers pick the neighbor advertising the lowest cost, then add this entry into its routing table
for re-advertisement. To find the shortest path, Distance Vector Algorithm is based on one of
two basic algorithms: the Bellman-Ford and the Dijkstra algorithms.
Routers that use this algorithm have to maintain the distance tables (which is a one-
dimension array -- "a vector"), which tell the distances and shortest path to sending packets to
each node in the network. The information in the distance table is always upd by exchanging
information with the neighboring nodes. The number of data in the table equals to that of all
nodes in networks (excluded itself). The columns of table represent the directly attached
neighbors whereas the rows represent all destinations in the network. Each data contains the
path for sending packets to each destination in the network and distance/or time to transmit on
that path (we call this as "cost"). The measurements in this algorithm are the number of hops,
latency, the number of outgoing packets, etc.
#include<stdio.h>
#include<stdlib.h>
void rout_table();
int d[10][10],via[10][10];
int i,j,k,l,m,n,g[10][10],temp[10][10],ch,cost;
int main()
{
printf("enter the value of no. of nodes\n");
scanf("%d",&n);
rout_table();
for(i=0;i<n;i++)
for(j=0;j<n;j++)
temp[i][j]=g[i][j];
for(i=0;i<n;i++)
for(j=0;j<n;j++)
via[i][j]=i;
while(1)
{
for(i=0;i<n;i++)
for(j=0;j<n;j++)
if(d[i][j])
for(k=0;k<n;k++)
if(g[i][j]+g[j][k]<g[i][k])
{
g[i][k]=g[i][j]+g[j][k];
via[i][k]=j;
}
for(i=0;i<n;i++)
{
printf("table for router %c\n" ,i+97);
for(j=0;j<n;j++)
printf("%c:: %d via %c\n" ,j+97,g[i][j],via[i][j]+97);
}
break;
}
}
void rout_table()
{
printf("\nEnter the routing table : \n");
printf("\t|");
for(i=1;i<=n;i++)
printf("%c\t",i+96);
printf("\n");
for(i=0;i<=n;i++)
printf("-------");
printf("\n");
for(i=0;i<n;i++)
{
printf("%c |",i+97);
for(j=0;j<n;j++)
{
scanf("%d",&g[i][j]);
if(g[i][j]!=999)
d[i][j]=1;
}
}
}
Output:
enter the value of no. of nodes
4
Enter the routing table:
|a b c d
-----------------------------------
a |0 5 1 4
b |5 0 6 2
c |1 6 0 3
d |4 2 3 0
2) Using TCP/IP sockets, write a client-server program to make client sending the file
name and the server to send back the contents of the requested file if present
Use of sockets:
Connection–based sockets communicate client-server: the server waits for a connection
from the client
Connectionless sockets are peer-to-peer: each process is symmetric.
Socket APIs
socket: creates a socket of a given domain, type, protocol (buy a phone)
bind: assigns a name to the socket (get a telephone number)
listen: specifies the number of pending connections that can be queued for a server
socket. (call waiting allowance)
accept: server accepts a connection request from a client (answer phone)
connect: client requests a connection request to a server (call)
Connection-based communication
Server performs the following actions
TCP-based sockets
socket API
#include<sys/types.h>
#include<sys /socket.h>
int socket(int domain, int type, int protocol) ;
Returns a file descriptor (called a socket ID) if successful, -1 otherwise. Note that the socket
returns a socket descriptor which is the same as a file descriptor.
The domain is AF_INET.
The type argument can be:
SOCK_STREAM: Establishes a virtual circuit for stream
SOCK_DGRAM: Establishes a datagram for communication
SOCK_SEQPACKET: Establishes a reliable, connection based, two way
communication with maximum message size. (This is not available on most machines.)
Mr. Prashant A, Faculty, Dept. of MCA, JNNCE 5
Computer Networks Lab
protocol is usually zero, so that type defines the connection within domain.
bind
#include <sys / types.h>
#include<sys / socket.h>
int bind(int sid, struct sockaddr *addrPtr, int len)
Where
sid: is the socket id
addrPtr: is a pointer to the address family dependent address structure
len: is the size of *addrPtr
Associates a socket id with an address to which other processes can connect. In internet
protocol the address is [ipNumber, portNumber]
sockaddr
For the internet family:
struct sockaddr_in {
sa_family_t sin_family; // = AF INET
in_port_t sin_port; // is a port number
struct in_addr sin_addr; // an IP address
}
listen
#include <sys / types.h>
#include <sys / socket.h>
int listen (int sid, int size) ;
Where size it the number of pending connection requests allowed (typically limited by Unix
kernels to 5). Returns the 0 on success, or -1 if failure.
accept
#include <sys / types.h>
#include <sys / socket.h>
int accept(int sid ,struct sockaddr *addrPtr , int *lenPtr )
send
#include <sys / types.h>
#include <sys / socket.h>
int send(int sid ,const char *bufferPtr ,int len ,int flag)
0: default
MSG OOB: out-of-bound message
MSG PEEK: look at message without removing
Shutdown
#include <sys / types.h>
#include <sys / socket.h>
int shutdown ( int sid , int how)
Disables sending (how=1 or how=2) or receiving (how=0 or how=2). Returns -1 on failure.
Connect
-this is the first of the client calls
#include <sys / types.h>
#include <sys / socket.h>
int connect ( int sid , struct sockaddr *addrPtr , int len)
Specifies the destination to form a connection with (addrPtr), and returns a 0 if successful, -1
otherwise.
Port usage
Note that the initiator of communications needs a fixed port to target communications.
This means that some ports must be reserved for these “well known” ports.
Port usage:
0-1023: These ports can only be binded to by root
1024-5000: well known ports
5001-64K-1: ephemeral ports
Client program:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
Mr. Prashant A, Faculty, Dept. of MCA, JNNCE 7
Computer Networks Lab
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<string.h>
#define SERV_TCP_PORT 9000
#define SERV_HOST_ADDR "127.0.0.1"
int main()
{ int sockfd;
struct sockaddr_in serv_addr,cli_addr;
char filename[100],buf[1000];
int n;
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=inet_addr(SERV_HOST_ADDR);
serv_addr.sin_port=htons(SERV_TCP_PORT);
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
printf("Client:cant open stream socket\n");
else
printf("Client:stream socket opened successfully\n");
if(connect(sockfd,(struct sockaddr *)&serv_addr,
sizeof(serv_addr))<0)
printf("Client:cant connect to server\n");
else
printf("Client:connected to server successfully\n");
printf("\n Enter the file name to be displayed :");
scanf("%s",filename);
write(sockfd,filename,strlen(filename));
printf("\n filename transfered to server\n");
n=read(sockfd,buf,1000);
if(n < 0)
printf("\n error reading from socket");
printf("\n Client : Displaying file content of %s\n",filename);
fputs(buf,stdout);
close(sockfd);
exit(0);
}
Server program:
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<fcntl.h>
#include<string.h>
#define SERV_TCP_PORT 9000
#define SERV_HOST_ADDR "127.0.0.1"
int main()
{ int sockfd,newsockfd,clilen;
struct sockaddr_in cli_addr,serv_addr;
char filename[25],buf[1000];
int n,m=0;
int fd;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
printf("server:cant open stream socket\n");
else
printf("server:stream socket opened successfully\n");
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port=htons(SERV_TCP_PORT);
if((bind(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)))<0)
printf("server:cant bind local address\n");
else
printf("server:bound to local address\n");
listen(sockfd,5);
printf("\n SERVER : Waiting for client...\n");
for(;;)
{
clilen=sizeof(cli_addr);
newsockfd=accept(sockfd,(struct sockaddr *) &cli_addr,&clilen);
if(newsockfd<0)
printf("server:accept error\n");
else
printf("server:accepted\n");
n=read(newsockfd,filename,25);
filename[n]='\0';
printf("\n SERVER : %s is found and ready to transfer\n",filename);
fd=open(filename,O_RDONLY);
n=read(fd,buf,1000);
buf[n]='\0';
write(newsockfd,buf,n);
printf("\n transfer success\n");
close(newsockfd);
exit(0);
}
}
3) Implement the above program using as message queues of FIFOs as IPC channel
Message Queues
Message queues are one of the three different types of System V IPC mechanisms. This
mechanism enables processes to send information to each other asynchronously. The word
asynchronous in the present context signifies that the sender process continues with its
execution without waiting for the receiver to receive or acknowledge the information. On the
other side, the receiver does not wait if no messages are there in the queue. The queue being
referred to here is the queue implemented and maintained by the kernel.
Let us now take a look at the system calls associated with this mechanism.
a. msgget: This, in a way similar to shmget, gets a message queue identifier. The format
is
int msgget(ket_t key, int msgflg);
The first argument is a unique key, which can be generated by using ftok algorithm.
The second argument is the flag which can be IPC_CREAT, IPC_PRIVATE, or one of
the other valid possibilities (see the man page); the permissions (read and/or write) are
logically ORed with the flags. msgget returns an identifier associated with the key. This
identifier can be used for further processing of the message queue associated with the
identifier.
b. msgctl: This controls the operations on the message queue. The format is
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
Here msqid is the message queue identifier returned by msgget. The second argument
is cmd, which indicates which action is to be taken on the message queue. The third
argument is a buffer of type struct msqid_ds. Each message queue has this structure
associated with it; it is composed of records for queues to be identified by the kernel. This
structure also defines the current status of the message queue. If one of the cmds is
IPC_SET, some fields in the msqid_ds structure (pointed by the third argument) will be
set to the specified values. See the man page for the details.
c. msgsnd: This is for sending messages. The format is
int msgsnd(int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg);
The first argument is the message queue identifier returned by msgget. The second
argument is a structure that the calling process allocates. A call to msgsnd appends a copy
of the message pointed to by msgp to the message queue identified by msqid. The third
argument is the size of the message text within the msgbuf structure. The fourth argument
is the flag that specify one of several actions to be taken as and when a specific situation
arises.
d. msgrcv: This is for receiving messages. The format is
ssize_t msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int
msgflg);
Besides the four arguments mentioned above for msgsnd, we also have msgtyp, which
specifies the type of message requested.
Mr. Prashant A, Faculty, Dept. of MCA, JNNCE 11
Computer Networks Lab
FIFO :
FIFOs (first in, first out) are similar to the working of pipes. One major feature of pipe
is that the data flowing through the communication medium is transient, that is, data once read
from the read descriptor cannot be read again. Also, if we write data continuously into the
write descriptor, then we will be able to read the data only in the order in which the data was
written. One can experiment with that by doing successive writes or reads to the respective
descriptors.
FIFOs also provide half-duplex flow of data just like pipes. The difference between
fifos and pipes is that the former is identified in the file system with a name, while the latter is
not. That is, fifos are named pipes. Fifos are identified by an access point which is a file within
the file system, whereas pipes are identified by an access point which is simply an allotted
inode. Another major difference between fifos and pipes is that fifos last throughout the life-
cycle of the system, while pipes last only during the life-cycle of the process in which they
were created. To make it more clear, fifos exist beyond the life of the process. Since they are
identified by the file system, they remain in the hierarchy until explicitly removed using
unlink, but pipes are inherited only by related processes, that is, processes which are
descendants of a single process.
Server Program
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
int main()
{ char filename[100],buf[300],buf1[300];
int num,num2,n,filesize,fl,fd,fd2;
mknod("fifo1",S_IFIFO | 0666,0);
mknod("fifo2",S_IFIFO | 0666,0);
printf("\n Server Online\n");
fd=open("fifo1",O_RDONLY);
printf("Client Online! Waiting for request...\n\n");
while(1)
{ num = read(fd,filename,100);
filename[num]='\0';
fl=open(filename,O_RDONLY);
printf("\n Sever: %s is found!\n transferring the contents\n",filename);
filesize=lseek(fl,0,2);
Mr. Prashant A, Faculty, Dept. of MCA, JNNCE 12
Computer Networks Lab
Client Program:
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
int main()
{ char filename[100],buf[300];
int num,num2,fl,fd,fd2;
mknod("fifo1",S_IFIFO | 0666,0);
mknod("fifo2",S_IFIFO | 0666,0);
fd=open("fifo1",O_WRONLY);
printf("CLient Online! \nCLIENT enter the path...\n");
scanf("%s",filename);
write(fd,filename,strlen(filename));
printf("\n waiting for reply...\n");
fd2=open("fifo2",O_RDONLY);
num2=read(fd2,buf,300);
buf[num2]='\0';
printf("\n File received ..the contents are...\n");
fputs(buf,stdout);
unlink("fifo1");
unlink("fifo2");
exit(1);
}
4) Write a program for simple RSA algorithm to encrypt and decrypt the data.
The RSA algorithm can be used for both public key encryption and digital signatures.
Its security is based on the difficulty of factoring large integers.
The RSA algorithm's efficiency requires a fast method for performing the modular
exponentiation operation. A less efficient, conventional method includes raising a number (the
input) to a power (the secret or public key of the algorithm, denoted e and d, respectively) and
taking the remainder of the division with N. A straight-forward implementation performs these
two steps of the operation sequentially: first, raise it to the power and second, apply modulo.
A very simple example of RSA encryption
This is an extremely simple example using numbers you can work out on a pocket
calculator (those of you over the age of 35 can probably even do it by hand on paper).
1. Select primes p = 11, q = 3.
2. n = pq = 11.3 = 33
phi = (p-1)(q-1) = 10.2 = 20
3. Choose e=3
Check gcd(e, p-1) = gcd(3, 10) = 1 (i.e. 3 and 10 have no common factors except 1),
and check gcd(e, q-1) = gcd(3, 2) = 1
therefore gcd(e, phi) = gcd(e, (p-1)(q-1)) = gcd(3, 20) = 1
4. Compute d such that ed ≡ 1 (mod phi)
i.e. compute d = e^-1 mod phi = 3^-1 mod 20
i.e. find a value for d such that phi divides (ed-1)
i.e. find d such that 20 divides 3d-1.
Simple testing (d = 1, 2, ...) gives d = 7
Check: ed-1 = 3.7 - 1 = 20, which is divisible by phi.
5. Public key = (n, e) = (33, 3)
Private key = (n, d) = (33, 7).
This is actually the smallest possible value for the modulus n for which the RSA
algorithm works.
Now say we want to encrypt the message m = 7,
c = m^e mod n = 7^3 mod 33 = 343 mod 33 = 13.
Hence the ciphertext c = 13.
To check decryption we compute
m' = c^d mod n = 13^7 mod 33 = 7.
Note that we don't have to calculate the full value of 13 to the power 7 here. We can make use
of the fact that a = bc mod n = (b mod n).(c mod n) mod n so we can break down a potentially
large number into its components and combine the results of easier, smaller calculations to
calculate the final value.
One way of calculating m' is as follows:-
m' = 13^7 mod 33 = 13^(3+3+1) mod 33 = 13^3.13^3.13 mod 33
= (13^3 mod 33).(13^3 mod 33).(13 mod 33) mod 33
Mr. Prashant A, Faculty, Dept. of MCA, JNNCE 14
Computer Networks Lab
m 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
c 29 24 28 14 21 22 23 30 16 20 15 7 2 6 25 32
Note that all 33 values of m (0 to 32) map to a unique code c in the same range in a sort
of random manner. In this case we have nine values of m that map to the same value of c -
these are known as unconcealed messages. m = 0 and 1 will always do this for any N, no
matter how large. But in practice, higher values shouldn't be a problem when we use large
values for N.
If we wanted to use this system to keep secrets, we could let A=2, B=3, ..., Z=27. (We
specifically avoid 0 and 1 here for the reason given above). Thus the plaintext message
"HELLOWORLD" would be represented by the set of integers m1, m2, ...
{9,6,13,13,16,24,16,19,13,5}
Using our table above, we obtain cipher text integers c1, c2, ...
{3,18,19,19,4,30,4,28,19,26}
Note that this example is no more secure than using a simple Caesar substitution cipher,
but it serves to illustrate a simple example of the mechanics of RSA encryption.
Remember that calculating m^e mod n is easy, but calculating the inverse c^-e mod n is very
difficult, well, for large n's anyway. However, if we can factor n into its prime factors p and q,
the solution becomes easy again, even for large n's. Obviously, if we can get hold of the secret
exponent d, the solution is easy, too.
Source Code:
#include<math.h>
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int gcd(long m,long n)
{
while(n!=0)
{
long r=m%n;
m=n;
n=r;
}
return m;
}
for(i=2;i<phi;i++)
if(gcd(i,phi)==1) break;
e=i;
for(i=2;i<phi;i++)
if((e*i-1)%phi==0)break;
d=i;
for(i=0;i<strlen(message);i++)
nummes[i]=message[i]-96;
nofelem=strlen(message);
for(i=0;i<nofelem;i++)
{
encrypted[i]=1;
for(j=0;j<e;j++)
encrypted[i] =(encrypted[i]*nummes[i])%n;
}
printf("\n Encrypted message\n");
for(i=0;i<nofelem;i++)
{
printf(" %ld ",encrypted[i]);
printf("%c",(char)(encrypted[i])+96);
}
for(i=0;i<nofelem;i++)
{
decrypted[i]=1;
for(j=0;j<d;j++)
decrypted[i]=(decrypted[i]*encrypted[i])%n;
}
printf("\n Decrypted message\n ");
for(i=0;i<nofelem;i++)
printf("%c",(char)(decrypted[i]+96));
return 0;
}
int main()
{ char msg[50];
system("clear");
printf("Enter The Message To Be Encrypted\n");
scanf("%s",msg);
rsa(msg);
return 0;
}
Output:
Enter the text:
hello
Enter the value of P and Q :
5
7
Encrypted Text is: 8 h 10 j 17 q 17 q 15 o
Decrypted Text is: hello
5) Write a program for Congestion control using the leaky bucket algorithm.
The main concept of the leaky bucket algorithm is that the output data flow remains
constant despite the variant input traffic, such as the water flow in a bucket with a small hole
at the bottom. In case the bucket contains water (or packets) then the output flow follows a
constant rate, while if the bucket is full any additional load will be lost because of spillover. In
a similar way if the bucket is empty the output will be zero.
From network perspective, leaky bucket consists of a finite queue (bucket) where all the
incoming packets are stored in case there is space in the queue, otherwise the packets are
discarded. In order to regulate the output flow, leaky bucket transmits one packet from the
queue in a fixed time (e.g. at every clock tick). In the following figure we can notice the main
rationale of leaky bucket algorithm, for both the two approaches (e.g. leaky bucket with water
(a) and with packets (b)).
Source Code:
#include<stdio.h>
#include<string.h>
#include<stdio.h>
int min(int x,int y)
{
if(x<y)
return x;
Mr. Prashant A, Faculty, Dept. of MCA, JNNCE 18
Computer Networks Lab
else
return y;
}
void main()
{
int drop=0,mini,nsec,cap,count=0,i,inp[25],process;
printf("Enter The Bucket Size\n");
scanf("%d",&cap);
printf("Enter The Operation Rate\n");
scanf("%d",&process);
printf("Enter The No. Of Seconds You Want To Stimulate\n");
scanf("%d",&nsec);
for(i=0;i<nsec;i++)
{
printf("Enter The Size Of The Packet Entering At %dsec\n",i+1);
scanf("%d",&inp[i]);
}
printf("\nSeconds|Packet Recieved|Packet Sent|PacketLeft|Packet Dropped|\n");
printf("--------------------------------------------------------------\n");
for(i=0;i<nsec;i++)
{
count+=inp[i];
if(count>cap)
{
drop=count-cap;
count=cap;
}
printf("%d",i+1);
printf("\t%d",inp[i]);
mini=min(count,process);
printf("\t\t%d",mini);
count=count-mini;
printf("\t\t%d",count);
printf("\t\t%d\n",drop);
drop=0;
}
for(;count!=0;i++)
{
if(count>cap)
{drop=count-cap;
count=cap;
}
printf("%d",i+1);
printf("\t0");
mini=min(count,process);
printf("\t\t%d",mini);
count=count-mini;
printf("\t\t%d",count);
printf("\t\t%d\n",drop);
}
}
Inputs :
Enter The Bucket Size
5
Enter The Operation Rate
2
Enter The No. Of Seconds You Want To Stimulate
3
Enter The Size Of The Packet Entering At 1 sec
5
Enter The Size Of The Packet Entering At 1 sec
4
Enter The Size Of The Packet Entering At 1 sec
3
Output:
Second|Packet Recieved|Packet Sent|Packet Left|Packet Dropped|
---------------------------------------------------------------------------------
1 5 2 3 0
2 4 2 3 2
3 3 2 3 1
4 0 2 1 0
5 0 1 0 0