1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2022 InnoGames GmbH
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 */
28
29#include <sys/cdefs.h>
30#include "opt_inet.h"
31#include "opt_inet6.h"
32
33#include <sys/param.h>
34#include <sys/errno.h>
35
36#include <netinet/in.h>
37
38#include <netinet6/ip6_var.h>
39#include <netinet6/scope6_var.h>
40
41#include <netpfil/pf/pfsync_nv.h>
42
43int
44pfsync_syncpeer_nvlist_to_sockaddr(const nvlist_t *nvl,
45    struct sockaddr_storage *sa)
46{
47	int af;
48
49	if (!nvlist_exists_number(nvl, "af"))
50		return (EINVAL);
51	if (!nvlist_exists_binary(nvl, "address"))
52		return (EINVAL);
53
54	af = nvlist_get_number(nvl, "af");
55
56	switch (af) {
57#ifdef INET
58	case AF_INET: {
59		struct sockaddr_in *in = (struct sockaddr_in *)sa;
60		size_t len;
61		const void *addr = nvlist_get_binary(nvl, "address", &len);
62		in->sin_family = af;
63		if (len != sizeof(*in))
64			return (EINVAL);
65
66		memcpy(in, addr, sizeof(*in));
67		break;
68	}
69#endif
70#ifdef INET6
71	case AF_INET6: {
72		struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa;
73		size_t len;
74		int error;
75		const void *addr = nvlist_get_binary(nvl, "address", &len);
76		in6->sin6_family = af;
77		if (len != sizeof(*in6))
78			return (EINVAL);
79
80		memcpy(in6, addr, sizeof(*in6));
81
82		error = sa6_embedscope(in6, V_ip6_use_defzone);
83		if (error)
84			return (error);
85
86		break;
87	}
88#endif
89	default:
90		return (EINVAL);
91	}
92
93	return (0);
94}
95
96nvlist_t *
97pfsync_sockaddr_to_syncpeer_nvlist(struct sockaddr_storage *sa)
98{
99	nvlist_t *nvl;
100
101	nvl = nvlist_create(0);
102	if (nvl == NULL) {
103		return (nvl);
104	}
105
106	switch (sa->ss_family) {
107#ifdef INET
108	case AF_INET: {
109		struct sockaddr_in *in = (struct sockaddr_in *)sa;
110		nvlist_add_number(nvl, "af", in->sin_family);
111		nvlist_add_binary(nvl, "address", in, sizeof(*in));
112		break;
113	}
114#endif
115#ifdef INET6
116	case AF_INET6: {
117		struct sockaddr_in6 *in6 = (struct sockaddr_in6 *)sa;
118		sa6_recoverscope(in6);
119		nvlist_add_number(nvl, "af", in6->sin6_family);
120		nvlist_add_binary(nvl, "address", in6, sizeof(*in6));
121		break;
122	}
123#endif
124	default:
125		return NULL;
126	}
127
128	return (nvl);
129}
130
131int
132pfsync_nvstatus_to_kstatus(const nvlist_t *nvl, struct pfsync_kstatus *status)
133{
134	struct sockaddr_storage addr;
135	int error;
136
137	if (!nvlist_exists_number(nvl, "maxupdates"))
138		return (EINVAL);
139	if (!nvlist_exists_number(nvl, "flags"))
140		return (EINVAL);
141
142	status->maxupdates = nvlist_get_number(nvl, "maxupdates");
143	status->version = nvlist_get_number(nvl, "version");
144	status->flags = nvlist_get_number(nvl, "flags");
145
146	if (nvlist_exists_string(nvl, "syncdev"))
147		strlcpy(status->syncdev, nvlist_get_string(nvl, "syncdev"),
148		    IFNAMSIZ);
149
150	if (nvlist_exists_nvlist(nvl, "syncpeer")) {
151		memset(&addr, 0, sizeof(addr));
152		if ((error = pfsync_syncpeer_nvlist_to_sockaddr(nvlist_get_nvlist(nvl, "syncpeer"), &addr)) != 0)
153			return (error);
154
155		status->syncpeer = addr;
156	} else {
157		memset(&status->syncpeer, 0, sizeof(status->syncpeer));
158	}
159
160	return (0);
161}
162