quagga: use AUTOBUFCHUNK as allocation unit
[olsrd.git] / lib / quagga / src / client.c
1 /*
2  * The olsr.org Optimized Link-State Routing daemon (olsrd)
3  *
4  * (c) by the OLSR project
5  *
6  * See our Git repository to find out who worked on this file
7  * and thus is a copyright holder on it.
8  *
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  *
15  * * Redistributions of source code must retain the above copyright
16  *   notice, this list of conditions and the following disclaimer.
17  * * Redistributions in binary form must reproduce the above copyright
18  *   notice, this list of conditions and the following disclaimer in
19  *   the documentation and/or other materials provided with the
20  *   distribution.
21  * * Neither the name of olsr.org, olsrd nor the names of its
22  *   contributors may be used to endorse or promote products derived
23  *   from this software without specific prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
28  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
29  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  *
38  * Visit http://www.olsr.org for more information.
39  *
40  * If you find this software useful feel free to make a donation
41  * to the project. For more information see the website or contact
42  * the copyright holders.
43  *
44  */
45
46 /* -------------------------------------------------------------------------
47  * File               : client.c
48  * Description        : zebra client functions
49  * ------------------------------------------------------------------------- */
50
51 #define HAVE_SOCKLEN_T
52
53 #include <sys/un.h>
54 #include <unistd.h>
55 #include <fcntl.h>
56 #include "defs.h"
57 #include "olsr.h"
58 #include "log.h"
59 #include "routing_table.h"
60 #include "common/autobuf.h"
61
62 #include "common.h"
63 #include "quagga.h"
64 #include "packet.h"
65 #include "client.h"
66
67 static void *my_realloc(void *, size_t, const char *);
68 static void zclient_connect(void);
69
70 static void *
71 my_realloc(void *buf, size_t s, const char *c)
72 {
73
74   buf = realloc(buf, s);
75   if (!buf) {
76     char buf2[1024];
77     snprintf(buf2, sizeof(buf2), "QUAGGA: Out of memory (%s): %s", c, strerror(errno));
78     olsr_exit(buf2, EXIT_FAILURE);
79   }
80
81   return buf;
82 }
83
84 static void
85 zclient_connect(void)
86 {
87   int ret;
88
89   union {
90     struct sockaddr_in sin;
91     struct sockaddr_un sun;
92   } sockaddr;
93
94   if (close(zebra.sock) < 0)
95     olsr_exit("QUAGGA: Could not close socket", EXIT_FAILURE);
96
97   zebra.sock = socket(zebra.port ? AF_INET : AF_UNIX, SOCK_STREAM, 0);
98
99   if (zebra.sock < 0)
100     olsr_exit("QUAGGA: Could not create socket", EXIT_FAILURE);
101
102   memset(&sockaddr, 0, sizeof sockaddr);
103
104   if (zebra.port) {
105     sockaddr.sin.sin_family = AF_INET;
106     sockaddr.sin.sin_port = htons(zebra.port);
107     sockaddr.sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
108     ret = connect(zebra.sock, (struct sockaddr *)&sockaddr.sin, sizeof sockaddr.sin);
109   } else {
110     sockaddr.sun.sun_family = AF_UNIX;
111     strscpy(sockaddr.sun.sun_path, zebra.sockpath, sizeof(sockaddr.sun.sun_path));
112     ret = connect(zebra.sock, (struct sockaddr *)&sockaddr.sun, sizeof sockaddr.sun);
113   }
114
115   if (ret < 0)
116     zebra.status &= ~STATUS_CONNECTED;
117   else
118     zebra.status |= STATUS_CONNECTED;
119
120 }
121
122 void
123 zclient_reconnect(void)
124 {
125   struct rt_entry *tmp;
126
127   zclient_connect();
128   if (!(zebra.status & STATUS_CONNECTED))
129     return;                     // try again next time
130
131   zebra_hello(ZEBRA_HELLO);
132   if (zebra.options & OPTION_EXPORT) {
133     OLSR_FOR_ALL_RT_ENTRIES(tmp) {
134       zebra_addroute(tmp);
135     }
136     OLSR_FOR_ALL_RT_ENTRIES_END(tmp);
137   }
138   zebra_redistribute(ZEBRA_REDISTRIBUTE_ADD);
139
140 }
141
142 int
143 zclient_write(unsigned char *options)
144 {
145   unsigned char *pnt;
146   uint16_t len;
147   int ret;
148
149   if (!(zebra.status & STATUS_CONNECTED))
150     return 0;
151
152   pnt = options;
153   memcpy(&len, pnt, sizeof len);
154
155   len = ntohs(len);
156
157   do {
158     ret = write(zebra.sock, pnt, len);
159     if (ret < 0) {
160       if ((errno == EINTR) || (errno == EAGAIN)) {
161         errno = 0;
162         ret = 0;
163         continue;
164       } else {
165         OLSR_PRINTF(1, "(QUAGGA) Disconnected from zebra.\n");
166         zebra.status &= ~STATUS_CONNECTED;
167         /* TODO: Remove HNAs added from redistribution */
168         free(options);
169         return -1;
170       }
171     }
172     pnt = pnt + ret;
173   }
174   while ((len -= ret));
175   free(options);
176
177   return 0;
178 }
179
180 unsigned char *
181 zclient_read(ssize_t * size)
182 {
183   unsigned char *buf;
184   ssize_t bytes, bufsize;
185   uint16_t length, offset;
186   int sockstatus;
187
188   /* initialize variables */
189   buf = NULL;
190   offset = 0;
191   *size = 0;
192   bufsize = 0;
193
194   /* save socket status and set non-blocking for read */
195   sockstatus = fcntl(zebra.sock, F_GETFL);
196   (void)fcntl(zebra.sock, F_SETFL, sockstatus|O_NONBLOCK);
197
198   /* read whole packages */
199   do {
200
201     /* (re)allocate buffer */
202     if (*size == bufsize) {
203       ssize_t start = bufsize;
204       bufsize += AUTOBUFCHUNK;
205       buf = my_realloc(buf, bufsize, "grow read buffer");
206       memset(&buf[start], 0, AUTOBUFCHUNK);
207     }
208
209     /* read from socket */
210     bytes = read(zebra.sock, buf + *size, bufsize - *size);
211     /* handle broken packet */
212     if (!bytes) {
213       /* restore socket status */
214       (void)fcntl(zebra.sock, F_SETFL, sockstatus);
215
216       free(buf);
217       return NULL;
218     }
219     /* handle no data available */
220     if (bytes < 0) {
221       /* handle disconnect */
222       if (errno != EAGAIN) {    // oops - we got disconnected
223         OLSR_PRINTF(1, "(QUAGGA) Disconnected from zebra\n");
224         zebra.status &= ~STATUS_CONNECTED;
225         /* TODO: Remove HNAs added from redistribution */
226       }
227
228       /* restore socket status */
229       (void)fcntl(zebra.sock, F_SETFL, sockstatus);
230
231       free(buf);
232       return NULL;
233     }
234
235     *size += bytes;
236
237     /* detect zebra packet fragmentation */
238     do {
239       memcpy(&length, buf + offset, sizeof(length));
240       length = ntohs(length);
241       offset += length;
242     }
243     while (*size >= (ssize_t) (offset + sizeof(length)));
244     /* set blocking socket on fragmented packet */
245     if (*size != offset)
246       (void)fcntl(zebra.sock, F_SETFL, sockstatus);
247
248   }
249   while (*size != offset);
250
251   /* restore socket status */
252   (void)fcntl(zebra.sock, F_SETFL, sockstatus);
253
254   return buf;
255 }
256
257 /*
258  * Local Variables:
259  * c-basic-offset: 2
260  * indent-tabs-mode: nil
261  * End:
262  */