* [PATCH 1/2] tproxy: add IPv6 support for socket match
2010-10-21 15:19 [PATCH 0/2] tproxy: add IPv6 support KOVACS Krisztian
@ 2010-10-21 15:19 ` KOVACS Krisztian
2010-10-21 15:19 ` [PATCH 2/2] tproxy: add IPv6 support to the TPROXY target KOVACS Krisztian
2010-10-25 16:04 ` [PATCH 0/2] tproxy: add IPv6 support Patrick McHardy
2 siblings, 0 replies; 4+ messages in thread
From: KOVACS Krisztian @ 2010-10-21 15:19 UTC (permalink / raw)
To: netfilter-devel; +Cc: Balazs Scheidler, Patrick McHardy
From: Balazs Scheidler <bazsi@balabit.hu>
This patch also adds userspace support for the --transparent mode
of matching, which the kernel already supports, but the iptables userspace
doesn't.
Signed-off-by: Balazs Scheidler <bazsi@balabit.hu>
Signed-off-by: KOVACS Krisztian <hidden@balabit.hu>
---
extensions/libxt_socket.c | 103 ++++++++++++++++++++++++++++++++---
extensions/libxt_socket.man | 6 ++
include/linux/netfilter/xt_socket.h | 12 ++++
3 files changed, 112 insertions(+), 9 deletions(-)
create mode 100644 include/linux/netfilter/xt_socket.h
diff --git a/extensions/libxt_socket.c b/extensions/libxt_socket.c
index 1490473..5705466 100644
--- a/extensions/libxt_socket.c
+++ b/extensions/libxt_socket.c
@@ -1,19 +1,106 @@
/*
* Shared library add-on to iptables to add early socket matching support.
*
- * Copyright (C) 2007 BalaBit IT Ltd.
+ * Copyright (C) 2007, 2009 BalaBit IT Ltd.
*/
+#include <stdio.h>
+#include <getopt.h>
#include <xtables.h>
+#include <linux/netfilter/xt_socket.h>
-static struct xtables_match socket_mt_reg = {
- .name = "socket",
- .version = XTABLES_VERSION,
- .family = NFPROTO_IPV4,
- .size = XT_ALIGN(0),
- .userspacesize = XT_ALIGN(0),
+static void socket_mt_help_v0(void)
+{
+ printf("socket match has no options.\n\n");
+}
+
+static void socket_mt_help_v1(void)
+{
+ printf("socket match options:\n"
+"--transparent Matches only if the socket's transparent option is set\n");
+}
+
+static const struct option socket_opts_v1[] = {
+ { "transparent", 0, NULL, '1' },
+ { }
+};
+
+static int socket_mt_parse_v0(int c, char **argv, int invert,
+ unsigned int *flags, const void *entry,
+ struct xt_entry_match **match)
+{
+ return 0;
+}
+
+static int socket_mt_parse_v1(int c, char **argv, int invert,
+ unsigned int *flags, const void *entry,
+ struct xt_entry_match **match)
+{
+ struct xt_socket_mtinfo1 *info = (void *) (*match)->data;
+
+ switch (c) {
+ case '1':
+ if (*flags)
+ xtables_error(PARAMETER_PROBLEM,
+ "Can't specify multiple --transparent");
+ info->flags |= XT_SOCKET_TRANSPARENT;
+ *flags = 1;
+ break;
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+static void socket_mt_check(unsigned int flags)
+{
+}
+
+static void socket_mt_print_v1(const void *ip,
+ const struct xt_entry_match *match,
+ int numeric)
+{
+ const struct xt_socket_mtinfo1 *info = (const void *)match->data;
+ printf("socket ");
+ if (info->flags & XT_SOCKET_TRANSPARENT)
+ printf("transparent ");
+}
+
+static void socket_mt_save_v1(const void *ip,
+ const struct xt_entry_match *match)
+{
+ const struct xt_socket_mtinfo1 *info = (const void *)match->data;
+
+ if (info->flags & XT_SOCKET_TRANSPARENT)
+ printf("--transparent ");
+}
+
+static struct xtables_match socket_matches[] = {
+ {
+ .name = "socket",
+ .revision = 0,
+ .version = XTABLES_VERSION,
+ .family = NFPROTO_IPV4,
+ .parse = socket_mt_parse_v0,
+ .final_check = socket_mt_check,
+ .help = socket_mt_help_v0,
+ },
+ {
+ .name = "socket",
+ .version = XTABLES_VERSION,
+ .revision = 1,
+ .family = NFPROTO_UNSPEC,
+ .size = XT_ALIGN(sizeof(struct xt_socket_mtinfo1)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_socket_mtinfo1)),
+ .parse = socket_mt_parse_v1,
+ .print = socket_mt_print_v1,
+ .save = socket_mt_save_v1,
+ .final_check = socket_mt_check,
+ .help = socket_mt_help_v1,
+ .extra_opts = socket_opts_v1,
+ }
};
void _init(void)
{
- xtables_register_match(&socket_mt_reg);
+ xtables_register_matches(socket_matches, ARRAY_SIZE(socket_matches));
}
diff --git a/extensions/libxt_socket.man b/extensions/libxt_socket.man
index 50c8854..edc9d75 100644
--- a/extensions/libxt_socket.man
+++ b/extensions/libxt_socket.man
@@ -1,2 +1,6 @@
This matches if an open socket can be found by doing a socket lookup on the
-packet.
+packet which doesn\'t listen on the \'any\' IP address (0.0.0.0).
+.TP
+.BI "\-\-transparent"
+Enables additional check, that the actual socket's transparent socket option
+has to be set.
diff --git a/include/linux/netfilter/xt_socket.h b/include/linux/netfilter/xt_socket.h
new file mode 100644
index 0000000..6f475b8
--- /dev/null
+++ b/include/linux/netfilter/xt_socket.h
@@ -0,0 +1,12 @@
+#ifndef _XT_SOCKET_H
+#define _XT_SOCKET_H
+
+enum {
+ XT_SOCKET_TRANSPARENT = 1 << 0,
+};
+
+struct xt_socket_mtinfo1 {
+ __u8 flags;
+};
+
+#endif /* _XT_SOCKET_H */
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 2/2] tproxy: add IPv6 support to the TPROXY target
2010-10-21 15:19 [PATCH 0/2] tproxy: add IPv6 support KOVACS Krisztian
2010-10-21 15:19 ` [PATCH 1/2] tproxy: add IPv6 support for socket match KOVACS Krisztian
@ 2010-10-21 15:19 ` KOVACS Krisztian
2010-10-25 16:04 ` [PATCH 0/2] tproxy: add IPv6 support Patrick McHardy
2 siblings, 0 replies; 4+ messages in thread
From: KOVACS Krisztian @ 2010-10-21 15:19 UTC (permalink / raw)
To: netfilter-devel; +Cc: Balazs Scheidler, Patrick McHardy
From: Balazs Scheidler <bazsi@balabit.hu>
Signed-off-by: Balazs Scheidler <bazsi@balabit.hu>
Signed-off-by: KOVACS Krisztian <hidden@balabit.hu>
---
extensions/libxt_TPROXY.c | 213 +++++++++++++++++++++++++++++------
include/linux/netfilter/xt_TPROXY.h | 7 +
2 files changed, 183 insertions(+), 37 deletions(-)
diff --git a/extensions/libxt_TPROXY.c b/extensions/libxt_TPROXY.c
index cd0b50a..74d122c 100644
--- a/extensions/libxt_TPROXY.c
+++ b/extensions/libxt_TPROXY.c
@@ -1,7 +1,7 @@
/*
* Shared library add-on to iptables to add TPROXY target support.
*
- * Copyright (C) 2002-2008 BalaBit IT Ltd.
+ * Copyright (C) 2002-2009 BalaBit IT Ltd.
*/
#include <getopt.h>
#include <stdbool.h>
@@ -15,8 +15,8 @@
#include <linux/netfilter/xt_TPROXY.h>
static const struct option tproxy_tg_opts[] = {
- {.name = "on-port", .has_arg = true, .val = '1'},
- {.name = "on-ip", .has_arg = true, .val = '2'},
+ {.name = "on-port", .has_arg = true, .val = '1'},
+ {.name = "on-ip", .has_arg = true, .val = '2'},
{.name = "tproxy-mark", .has_arg = true, .val = '3'},
XT_GETOPT_TABLEEND,
};
@@ -36,44 +36,64 @@ static void tproxy_tg_help(void)
" --tproxy-mark value[/mask] Mark packets with the given value/mask\n\n");
}
-static void parse_tproxy_lport(const char *s, struct xt_tproxy_target_info *info)
+static void parse_tproxy_lport(const char *s, unsigned short *lport)
{
- unsigned int lport;
+ unsigned int value;
- if (xtables_strtoui(s, NULL, &lport, 0, UINT16_MAX))
- info->lport = htons(lport);
+ if (xtables_strtoui(s, NULL, &value, 0, UINT16_MAX))
+ *lport = htons(value);
else
xtables_param_act(XTF_BAD_VALUE, "TPROXY", "--on-port", s);
}
-static void parse_tproxy_laddr(const char *s, struct xt_tproxy_target_info *info)
+static void parse_tproxy_laddr_v0(const char *s, __be32 *laddr)
{
- struct in_addr *laddr;
+ struct in_addr *ina;
- if ((laddr = xtables_numeric_to_ipaddr(s)) == NULL)
+ if ((ina = xtables_numeric_to_ipaddr(s)) == NULL)
xtables_param_act(XTF_BAD_VALUE, "TPROXY", "--on-ip", s);
- info->laddr = laddr->s_addr;
+ *laddr = ina->s_addr;
}
-static void parse_tproxy_mark(char *s, struct xt_tproxy_target_info *info)
+static void parse_tproxy_laddr(const char *s, int family, union nf_inet_addr *laddr)
+{
+
+ if (family == NFPROTO_IPV6) {
+ struct in6_addr *addr6;
+
+ if ((addr6 = xtables_numeric_to_ip6addr(s))) {
+ laddr->in6 = *addr6;
+ } else {
+ xtables_param_act(XTF_BAD_VALUE, "TPROXY", "--on-ip", s);
+ }
+ } else {
+ struct in_addr *addr;
+
+ if ((addr = xtables_numeric_to_ipaddr(s))) {
+ laddr->in = *addr;
+ } else {
+ xtables_param_act(XTF_BAD_VALUE, "TPROXY", "--on-ip", s);
+ }
+
+ }
+}
+
+static void parse_tproxy_mark(char *s, unsigned int *value, unsigned int *mask)
{
- unsigned int value, mask = UINT32_MAX;
char *end;
- if (!xtables_strtoui(s, &end, &value, 0, UINT32_MAX))
+ *mask = UINT32_MAX;
+ if (!xtables_strtoui(s, &end, value, 0, UINT32_MAX))
xtables_param_act(XTF_BAD_VALUE, "TPROXY", "--tproxy-mark", s);
if (*end == '/')
- if (!xtables_strtoui(end + 1, &end, &mask, 0, UINT32_MAX))
+ if (!xtables_strtoui(end + 1, &end, mask, 0, UINT32_MAX))
xtables_param_act(XTF_BAD_VALUE, "TPROXY", "--tproxy-mark", s);
if (*end != '\0')
xtables_param_act(XTF_BAD_VALUE, "TPROXY", "--tproxy-mark", s);
-
- info->mark_mask = mask;
- info->mark_value = value;
}
-static int tproxy_tg_parse(int c, char **argv, int invert, unsigned int *flags,
+static int tproxy_tg_parse_v0(int c, char **argv, int invert, unsigned int *flags,
const void *entry, struct xt_entry_target **target)
{
struct xt_tproxy_target_info *tproxyinfo = (void *)(*target)->data;
@@ -82,19 +102,19 @@ static int tproxy_tg_parse(int c, char **argv, int invert, unsigned int *flags,
case '1':
xtables_param_act(XTF_ONLY_ONCE, "TPROXY", "--on-port", *flags & PARAM_ONPORT);
xtables_param_act(XTF_NO_INVERT, "TPROXY", "--on-port", invert);
- parse_tproxy_lport(optarg, tproxyinfo);
+ parse_tproxy_lport(optarg, &tproxyinfo->lport);
*flags |= PARAM_ONPORT;
return 1;
case '2':
xtables_param_act(XTF_ONLY_ONCE, "TPROXY", "--on-ip", *flags & PARAM_ONIP);
xtables_param_act(XTF_NO_INVERT, "TPROXY", "--on-ip", invert);
- parse_tproxy_laddr(optarg, tproxyinfo);
+ parse_tproxy_laddr_v0(optarg, &tproxyinfo->laddr);
*flags |= PARAM_ONIP;
return 1;
case '3':
xtables_param_act(XTF_ONLY_ONCE, "TPROXY", "--tproxy-mark", *flags & PARAM_MARK);
xtables_param_act(XTF_NO_INVERT, "TPROXY", "--tproxy-mark", invert);
- parse_tproxy_mark(optarg, tproxyinfo);
+ parse_tproxy_mark(optarg, &tproxyinfo->mark_value, &tproxyinfo->mark_mask);
*flags |= PARAM_MARK;
return 1;
}
@@ -102,6 +122,47 @@ static int tproxy_tg_parse(int c, char **argv, int invert, unsigned int *flags,
return 0;
}
+static int tproxy_tg_parse_v1(int family, int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_target **target)
+{
+ struct xt_tproxy_target_info_v1 *tproxyinfo = (void *)(*target)->data;
+
+ switch (c) {
+ case '1':
+ xtables_param_act(XTF_ONLY_ONCE, "TPROXY", "--on-port", *flags & PARAM_ONPORT);
+ xtables_param_act(XTF_NO_INVERT, "TPROXY", "--on-port", invert);
+ parse_tproxy_lport(optarg, &tproxyinfo->lport);
+ *flags |= PARAM_ONPORT;
+ return 1;
+ case '2':
+ xtables_param_act(XTF_ONLY_ONCE, "TPROXY", "--on-ip", *flags & PARAM_ONIP);
+ xtables_param_act(XTF_NO_INVERT, "TPROXY", "--on-ip", invert);
+ parse_tproxy_laddr(optarg, family, &tproxyinfo->laddr);
+ *flags |= PARAM_ONIP;
+ return 1;
+ case '3':
+ xtables_param_act(XTF_ONLY_ONCE, "TPROXY", "--tproxy-mark", *flags & PARAM_MARK);
+ xtables_param_act(XTF_NO_INVERT, "TPROXY", "--tproxy-mark", invert);
+ parse_tproxy_mark(optarg, &tproxyinfo->mark_value, &tproxyinfo->mark_mask);
+ *flags |= PARAM_MARK;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int tproxy_tg_parse4_v1(int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_target **target)
+{
+ return tproxy_tg_parse_v1(NFPROTO_IPV4, c, argv, invert, flags, entry, target);
+}
+
+static int tproxy_tg_parse6_v1(int c, char **argv, int invert, unsigned int *flags,
+ const void *entry, struct xt_entry_target **target)
+{
+ return tproxy_tg_parse_v1(NFPROTO_IPV6, c, argv, invert, flags, entry, target);
+}
+
static void tproxy_tg_check(unsigned int flags)
{
if (!(flags & PARAM_ONPORT))
@@ -109,7 +170,7 @@ static void tproxy_tg_check(unsigned int flags)
"TPROXY target: Parameter --on-port is required");
}
-static void tproxy_tg_print(const void *ip, const struct xt_entry_target *target,
+static void tproxy_tg_print_v0(const void *ip, const struct xt_entry_target *target,
int numeric)
{
const struct xt_tproxy_target_info *info = (const void *)target->data;
@@ -119,7 +180,31 @@ static void tproxy_tg_print(const void *ip, const struct xt_entry_target *target
(unsigned int)info->mark_mask);
}
-static void tproxy_tg_save(const void *ip, const struct xt_entry_target *target)
+static void tproxy_tg_print_v1(int family, const void *ip, const struct xt_entry_target *target,
+ int numeric)
+{
+ const struct xt_tproxy_target_info_v1 *info = (const void *)target->data;
+ printf("TPROXY redirect %s:%u mark 0x%x/0x%x",
+ family == AF_INET
+ ? xtables_ipaddr_to_numeric(&info->laddr.in)
+ : xtables_ip6addr_to_numeric(&info->laddr.in6),
+ ntohs(info->lport), (unsigned int)info->mark_value,
+ (unsigned int)info->mark_mask);
+}
+
+static void tproxy_tg_print4_v1(const void *ip, const struct xt_entry_target *target,
+ int numeric)
+{
+ return tproxy_tg_print_v1(NFPROTO_IPV4, ip, target, numeric);
+}
+
+static void tproxy_tg_print6_v1(const void *ip, const struct xt_entry_target *target,
+ int numeric)
+{
+ return tproxy_tg_print_v1(NFPROTO_IPV6, ip, target, numeric);
+}
+
+static void tproxy_tg_save_v0(const void *ip, const struct xt_entry_target *target)
{
const struct xt_tproxy_target_info *info = (const void *)target->data;
@@ -130,21 +215,75 @@ static void tproxy_tg_save(const void *ip, const struct xt_entry_target *target)
(unsigned int)info->mark_value, (unsigned int)info->mark_mask);
}
-static struct xtables_target tproxy_tg_reg = {
- .name = "TPROXY",
- .family = NFPROTO_IPV4,
- .version = XTABLES_VERSION,
- .size = XT_ALIGN(sizeof(struct xt_tproxy_target_info)),
- .userspacesize = XT_ALIGN(sizeof(struct xt_tproxy_target_info)),
- .help = tproxy_tg_help,
- .parse = tproxy_tg_parse,
- .final_check = tproxy_tg_check,
- .print = tproxy_tg_print,
- .save = tproxy_tg_save,
- .extra_opts = tproxy_tg_opts,
+static void tproxy_tg_save_v1(int family, const void *ip, const struct xt_entry_target *target)
+{
+ const struct xt_tproxy_target_info_v1 *info = (const void *)target->data;
+
+ printf("--on-port %u ", ntohs(info->lport));
+ printf("--on-ip %s ",
+ family == AF_INET
+ ? xtables_ipaddr_to_numeric(&info->laddr.in)
+ : xtables_ip6addr_to_numeric(&info->laddr.in6));
+ printf("--tproxy-mark 0x%x/0x%x ",
+ (unsigned int)info->mark_value, (unsigned int)info->mark_mask);
+}
+
+static void tproxy_tg_save4_v1(const void *ip, const struct xt_entry_target *target)
+{
+ return tproxy_tg_save_v1(NFPROTO_IPV4, ip, target);
+}
+
+static void tproxy_tg_save6_v1(const void *ip, const struct xt_entry_target *target)
+{
+ return tproxy_tg_save_v1(NFPROTO_IPV6, ip, target);
+}
+
+
+static struct xtables_target tproxy_tg_reg[] = {
+ {
+ .name = "TPROXY",
+ .family = NFPROTO_IPV4,
+ .version = XTABLES_VERSION,
+ .size = XT_ALIGN(sizeof(struct xt_tproxy_target_info)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_tproxy_target_info)),
+ .help = tproxy_tg_help,
+ .parse = tproxy_tg_parse_v0,
+ .final_check = tproxy_tg_check,
+ .print = tproxy_tg_print_v0,
+ .save = tproxy_tg_save_v0,
+ .extra_opts = tproxy_tg_opts,
+ },
+ {
+ .name = "TPROXY",
+ .family = NFPROTO_IPV4,
+ .version = XTABLES_VERSION,
+ .revision = 1,
+ .size = XT_ALIGN(sizeof(struct xt_tproxy_target_info_v1)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_tproxy_target_info_v1)),
+ .help = tproxy_tg_help,
+ .parse = tproxy_tg_parse4_v1,
+ .final_check = tproxy_tg_check,
+ .print = tproxy_tg_print4_v1,
+ .save = tproxy_tg_save4_v1,
+ .extra_opts = tproxy_tg_opts,
+ },
+ {
+ .name = "TPROXY",
+ .family = NFPROTO_IPV6,
+ .version = XTABLES_VERSION,
+ .revision = 1,
+ .size = XT_ALIGN(sizeof(struct xt_tproxy_target_info_v1)),
+ .userspacesize = XT_ALIGN(sizeof(struct xt_tproxy_target_info_v1)),
+ .help = tproxy_tg_help,
+ .parse = tproxy_tg_parse6_v1,
+ .final_check = tproxy_tg_check,
+ .print = tproxy_tg_print6_v1,
+ .save = tproxy_tg_save6_v1,
+ .extra_opts = tproxy_tg_opts,
+ },
};
void _init(void)
{
- xtables_register_target(&tproxy_tg_reg);
+ xtables_register_targets(tproxy_tg_reg, ARRAY_SIZE(tproxy_tg_reg));
}
diff --git a/include/linux/netfilter/xt_TPROXY.h b/include/linux/netfilter/xt_TPROXY.h
index 152e8f9..28ff0e8 100644
--- a/include/linux/netfilter/xt_TPROXY.h
+++ b/include/linux/netfilter/xt_TPROXY.h
@@ -11,4 +11,11 @@ struct xt_tproxy_target_info {
__be16 lport;
};
+struct xt_tproxy_target_info_v1 {
+ u_int32_t mark_mask;
+ u_int32_t mark_value;
+ union nf_inet_addr laddr;
+ __be16 lport;
+};
+
#endif /* _XT_TPROXY_H_target */
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [PATCH 0/2] tproxy: add IPv6 support
@ 2010-10-21 15:19 KOVACS Krisztian
2010-10-21 15:19 ` [PATCH 1/2] tproxy: add IPv6 support for socket match KOVACS Krisztian
` (2 more replies)
0 siblings, 3 replies; 4+ messages in thread
From: KOVACS Krisztian @ 2010-10-21 15:19 UTC (permalink / raw)
To: netfilter-devel; +Cc: Balazs Scheidler, Patrick McHardy
This series adds IPv6 support to the socket match and the TPROXY target.
---
Balazs Scheidler (2):
tproxy: add IPv6 support for socket match
tproxy: add IPv6 support to the TPROXY target
extensions/libxt_TPROXY.c | 213 +++++++++++++++++++++++++++++------
extensions/libxt_socket.c | 103 ++++++++++++++++-
extensions/libxt_socket.man | 6 +
include/linux/netfilter/xt_TPROXY.h | 7 +
include/linux/netfilter/xt_socket.h | 12 ++
5 files changed, 295 insertions(+), 46 deletions(-)
create mode 100644 include/linux/netfilter/xt_socket.h
--
KOVACS Krisztian
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: [PATCH 0/2] tproxy: add IPv6 support
2010-10-21 15:19 [PATCH 0/2] tproxy: add IPv6 support KOVACS Krisztian
2010-10-21 15:19 ` [PATCH 1/2] tproxy: add IPv6 support for socket match KOVACS Krisztian
2010-10-21 15:19 ` [PATCH 2/2] tproxy: add IPv6 support to the TPROXY target KOVACS Krisztian
@ 2010-10-25 16:04 ` Patrick McHardy
2 siblings, 0 replies; 4+ messages in thread
From: Patrick McHardy @ 2010-10-25 16:04 UTC (permalink / raw)
To: KOVACS Krisztian; +Cc: netfilter-devel, Balazs Scheidler
Am 21.10.2010 17:19, schrieb KOVACS Krisztian:
> This series adds IPv6 support to the socket match and the TPROXY target.
Thanks, I'll add this to the iptables-next branch once I've released
the iptables 1.4.10 version.
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2010-10-25 16:04 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-10-21 15:19 [PATCH 0/2] tproxy: add IPv6 support KOVACS Krisztian
2010-10-21 15:19 ` [PATCH 1/2] tproxy: add IPv6 support for socket match KOVACS Krisztian
2010-10-21 15:19 ` [PATCH 2/2] tproxy: add IPv6 support to the TPROXY target KOVACS Krisztian
2010-10-25 16:04 ` [PATCH 0/2] tproxy: add IPv6 support Patrick McHardy
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.