1 /* $Id: upnpredirect.c,v 1.1 2008-09-15 12:28:52 winfred Exp $ */
3 * http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
4 * (c) 2006-2007 Thomas Bernard
5 * This software is subject to the conditions detailed
6 * in the LICENCE file provided within the distribution */
11 #include <sys/types.h>
12 #include <sys/socket.h>
13 #include <netinet/in.h>
15 #include <arpa/inet.h>
21 #include "upnpredirect.h"
22 #include "upnpglobalvars.h"
23 #include "upnpevents.h"
24 #if defined(USE_NETFILTER)
25 #include "netfilter/iptcrdr.h"
28 #include "pf/obsdrdr.h"
31 #include "ipf/ipfrdr.h"
33 #ifdef USE_MINIUPNPDCTL
37 #ifdef ENABLE_LEASEFILE
41 #ifdef ENABLE_LEASEFILE
42 static int lease_file_add( unsigned short eport, const char * iaddr, unsigned short iport, int proto, const char * desc)
46 if (lease_file == NULL) return 0;
48 fd = fopen( lease_file, "a");
50 syslog(LOG_ERR, "could not open lease file: %s", lease_file);
54 /* FIXME: what if desc contains ':'? */
55 fprintf( fd, "%s:%u:%s:%u:%s\n", ((proto==IPPROTO_TCP)?"TCP":"UDP"), eport, iaddr, iport, desc);
61 static int lease_file_remove( unsigned short eport, int proto)
67 char tmpfilename[128];
68 int str_size, buf_size;
71 if (lease_file == NULL) return 0;
73 if (strlen( lease_file) + 7 > sizeof(tmpfilename)) {
74 syslog(LOG_ERR, "Lease filename is too long");
78 strncpy( tmpfilename, lease_file, sizeof(tmpfilename) );
79 strncat( tmpfilename, "XXXXXX", sizeof(tmpfilename) - strlen(tmpfilename));
81 fd = fopen( lease_file, "r");
86 snprintf( str, sizeof(str), "%s:%u", ((proto==IPPROTO_TCP)?"TCP":"UDP"), eport);
87 str_size = strlen(str);
89 tmp = mkstemp(tmpfilename);
92 syslog(LOG_ERR, "could not open temporary lease file");
95 fchmod(tmp, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
96 fdt = fdopen(tmp, "a");
98 buf[sizeof(buf)-1] = 0;
99 while( fgets( buf, sizeof(buf)-1, fd) != NULL) {
100 buf_size = strlen(buf);
102 if (buf_size < str_size || strncmp( str, buf, str_size)!=0) {
103 fwrite(buf, buf_size, 1, fdt);
109 if (rename( tmpfilename, lease_file) < 0) {
110 syslog(LOG_ERR, "could not rename temporary lease file to %s", lease_file);
111 remove( tmpfilename);
120 * convert the string "UDP" or "TCP" to IPPROTO_UDP and IPPROTO_UDP */
122 proto_atoi(const char * protocol)
124 int proto = IPPROTO_TCP;
125 if(strcmp(protocol, "UDP") == 0)
131 * calls OS/fw dependant implementation of the redirection.
132 * protocol should be the string "TCP" or "UDP"
133 * returns: 0 on success
134 * -1 failed to redirect
135 * -2 already redirected
136 * -3 permission check failed
139 upnp_redirect(unsigned short eport,
140 const char * iaddr, unsigned short iport,
141 const char * protocol, const char * desc)
145 unsigned short iport_old;
146 struct in_addr address;
147 proto = proto_atoi(protocol);
148 if(inet_aton(iaddr, &address) < 0)
150 syslog(LOG_ERR, "inet_aton(%s) : %m", iaddr);
154 if(!check_upnp_rule_against_permissions(upnppermlist, num_upnpperm,
155 eport, address, iport))
157 syslog(LOG_INFO, "redirection permission check failed for "
158 "%hu->%s:%hu %s", eport, iaddr, iport, protocol);
161 r = get_redirect_rule(ext_if_name, eport, proto,
162 iaddr_old, sizeof(iaddr_old), &iport_old, 0, 0, 0, 0);
165 /* if existing redirect rule matches redirect request return success
166 * xbox 360 does not keep track of the port it redirects and will
167 * redirect another port when receiving ConflictInMappingEntry */
168 if(strcmp(iaddr,iaddr_old)==0 && iport==iport_old)
170 syslog(LOG_INFO, "ignoring redirect request as it matches existing redirect");
175 syslog(LOG_INFO, "port %hu protocol %s already redirected to %s:%hu",
176 eport, protocol, iaddr_old, iport_old);
182 syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s",
183 eport, iaddr, iport, protocol, desc);
184 return upnp_redirect_internal(eport, iaddr, iport, proto, desc);
186 if(add_redirect_rule2(ext_if_name, eport, iaddr, iport, proto, desc) < 0)
191 syslog(LOG_INFO, "creating pass rule to %s:%hu protocol %s for: %s",
192 iaddr, iport, protocol, desc);
193 if(add_filter_rule2(ext_if_name, iaddr, eport, iport, proto, desc) < 0)
195 /* clean up the redirect rule */
196 #if !defined(__linux__)
197 delete_redirect_rule(ext_if_name, eport, proto);
208 upnp_redirect_internal(unsigned short eport,
209 const char * iaddr, unsigned short iport,
210 int proto, const char * desc)
212 /*syslog(LOG_INFO, "redirecting port %hu to %s:%hu protocol %s for: %s",
213 eport, iaddr, iport, protocol, desc); */
214 if(add_redirect_rule2(ext_if_name, eport, iaddr, iport, proto, desc) < 0)
219 #ifdef ENABLE_LEASEFILE
220 lease_file_add( eport, iaddr, iport, proto, desc);
222 /* syslog(LOG_INFO, "creating pass rule to %s:%hu protocol %s for: %s",
223 iaddr, iport, protocol, desc);*/
224 if(add_filter_rule2(ext_if_name, iaddr, eport, iport, proto, desc) < 0)
226 /* clean up the redirect rule */
227 #if !defined(__linux__)
228 delete_redirect_rule(ext_if_name, eport, proto);
233 upnp_event_var_change_notify(EWanIPC);
241 upnp_get_redirection_infos(unsigned short eport, const char * protocol,
242 unsigned short * iport,
243 char * iaddr, int iaddrlen,
244 char * desc, int desclen)
246 if(desc && (desclen > 0))
248 return get_redirect_rule(ext_if_name, eport, proto_atoi(protocol),
249 iaddr, iaddrlen, iport, desc, desclen, 0, 0);
253 upnp_get_redirection_infos_by_index(int index,
254 unsigned short * eport, char * protocol,
255 unsigned short * iport,
256 char * iaddr, int iaddrlen,
257 char * desc, int desclen)
259 /*char ifname[IFNAMSIZ];*/
262 if(desc && (desclen > 0))
264 if(get_redirect_rule_by_index(index, 0/*ifname*/, eport, iaddr, iaddrlen,
265 iport, &proto, desc, desclen, 0, 0) < 0)
269 if(proto == IPPROTO_TCP)
270 memcpy(protocol, "TCP", 4);
272 memcpy(protocol, "UDP", 4);
278 _upnp_delete_redir(unsigned short eport, int proto)
281 #if defined(__linux__)
282 r = delete_redirect_and_filter_rules(eport, proto);
284 r = delete_redirect_rule(ext_if_name, eport, proto);
285 delete_filter_rule(ext_if_name, eport, proto);
287 #ifdef ENABLE_LEASEFILE
288 lease_file_remove( eport, proto);
292 upnp_event_var_change_notify(EWanIPC);
298 upnp_delete_redirection(unsigned short eport, const char * protocol)
300 syslog(LOG_INFO, "removing redirect rule port %hu %s", eport, protocol);
301 return _upnp_delete_redir(eport, proto_atoi(protocol));
304 /* upnp_get_portmapping_number_of_entries()
305 * TODO: improve this code */
307 upnp_get_portmapping_number_of_entries()
310 unsigned short eport, iport;
311 char protocol[4], iaddr[32], desc[64];
313 protocol[0] = '\0'; iaddr[0] = '\0'; desc[0] = '\0';
314 r = upnp_get_redirection_infos_by_index(n, &eport, protocol, &iport,
315 iaddr, sizeof(iaddr),
316 desc, sizeof(desc) );
322 /* functions used to remove unused rules */
324 get_upnp_rules_state_list(int max_rules_number_target)
326 char ifname[IFNAMSIZ];
328 unsigned short iport;
329 struct rule_state * tmp;
330 struct rule_state * list = 0;
332 tmp = malloc(sizeof(struct rule_state));
335 while(get_redirect_rule_by_index(i, ifname, &tmp->eport, 0, 0,
336 &iport, &proto, 0, 0,
337 &tmp->packets, &tmp->bytes) >= 0)
339 tmp->proto = (short)proto;
340 /* add tmp to list */
343 /* prepare next iteration */
345 tmp = malloc(sizeof(struct rule_state));
350 /* return empty list if not enough redirections */
351 if(i<=max_rules_number_target)
363 remove_unused_rules(struct rule_state * list)
365 char ifname[IFNAMSIZ];
366 unsigned short iport;
367 struct rule_state * tmp;
373 /* remove the rule if no traffic has used it */
374 if(get_redirect_rule(ifname, list->eport, list->proto,
375 0, 0, &iport, 0, 0, &packets, &bytes) >= 0)
377 if(packets == list->packets && bytes == list->bytes)
379 _upnp_delete_redir(list->eport, list->proto);
388 syslog(LOG_NOTICE, "removed %d unused rules", n);
392 /* stuff for miniupnpdctl */
393 #ifdef USE_MINIUPNPDCTL
395 write_ruleset_details(int s)
397 char ifname[IFNAMSIZ];
399 unsigned short eport, iport;
407 while(get_redirect_rule_by_index(i, ifname, &eport, iaddr, sizeof(iaddr),
408 &iport, &proto, desc, sizeof(desc),
409 &packets, &bytes) >= 0)
411 n = snprintf(buffer, sizeof(buffer), "%2d %s %s %hu->%s:%hu "
413 i, ifname, proto==IPPROTO_TCP?"TCP":"UDP",
414 eport, iaddr, iport, desc, packets, bytes);