Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
54dd43a6
Commit
54dd43a6
authored
Jun 24, 2003
by
Harald Welte
Committed by
David S. Miller
Jun 24, 2003
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
`cat msg`
parent
e024ba4b
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
62 additions
and
37 deletions
+62
-37
include/linux/netfilter_ipv4/listhelp.h
include/linux/netfilter_ipv4/listhelp.h
+16
-0
include/linux/netfilter_ipv4/lockhelp.h
include/linux/netfilter_ipv4/lockhelp.h
+7
-7
net/ipv4/netfilter/ip_conntrack_core.c
net/ipv4/netfilter/ip_conntrack_core.c
+14
-11
net/ipv4/netfilter/ip_conntrack_ftp.c
net/ipv4/netfilter/ip_conntrack_ftp.c
+2
-2
net/ipv4/netfilter/ip_conntrack_irc.c
net/ipv4/netfilter/ip_conntrack_irc.c
+2
-2
net/ipv4/netfilter/ip_nat_amanda.c
net/ipv4/netfilter/ip_nat_amanda.c
+1
-1
net/ipv4/netfilter/ip_nat_core.c
net/ipv4/netfilter/ip_nat_core.c
+13
-5
net/ipv4/netfilter/ip_nat_ftp.c
net/ipv4/netfilter/ip_nat_ftp.c
+1
-1
net/ipv4/netfilter/ip_nat_snmp_basic.c
net/ipv4/netfilter/ip_nat_snmp_basic.c
+4
-4
net/ipv6/netfilter/ip6_tables.c
net/ipv6/netfilter/ip6_tables.c
+2
-4
No files found.
include/linux/netfilter_ipv4/listhelp.h
View file @
54dd43a6
...
@@ -39,6 +39,22 @@
...
@@ -39,6 +39,22 @@
(type)__i; \
(type)__i; \
})
})
/* Just like LIST_FIND but we search backwards */
#define LIST_FIND_B(head, cmpfn, type, args...) \
({ \
const struct list_head *__i = (head); \
\
ASSERT_READ_LOCK(head); \
do { \
__i = __i->prev; \
if (__i == (head)) { \
__i = NULL; \
break; \
} \
} while (!cmpfn((const type)__i , ## args)); \
(type)__i; \
})
static
inline
int
static
inline
int
__list_cmp_same
(
const
void
*
p1
,
const
void
*
p2
)
{
return
p1
==
p2
;
}
__list_cmp_same
(
const
void
*
p1
,
const
void
*
p2
)
{
return
p1
==
p2
;
}
...
...
include/linux/netfilter_ipv4/lockhelp.h
View file @
54dd43a6
...
@@ -42,22 +42,22 @@ do { if (atomic_read(&(l)->locked_by) == smp_processor_id()) \
...
@@ -42,22 +42,22 @@ do { if (atomic_read(&(l)->locked_by) == smp_processor_id()) \
printk("ASSERT %s:%u %s locked\n", __FILE__, __LINE__, #l); \
printk("ASSERT %s:%u %s locked\n", __FILE__, __LINE__, #l); \
} while(0)
} while(0)
/* Write locked OK as well. */
\
/* Write locked OK as well. */
#define MUST_BE_READ_LOCKED(l) \
#define MUST_BE_READ_LOCKED(l) \
do { if (!((l)->read_locked_map & (1 << smp_processor_id())) \
do { if (!((l)->read_locked_map & (1
UL
<< smp_processor_id())) \
&& !((l)->write_locked_map & (1 << smp_processor_id()))) \
&& !((l)->write_locked_map & (1
UL
<< smp_processor_id()))) \
printk("ASSERT %s:%u %s not readlocked\n", __FILE__, __LINE__, #l); \
printk("ASSERT %s:%u %s not readlocked\n", __FILE__, __LINE__, #l); \
} while(0)
} while(0)
#define MUST_BE_WRITE_LOCKED(l) \
#define MUST_BE_WRITE_LOCKED(l) \
do { if (!((l)->write_locked_map & (1
<< smp_processor_id())))
\
do { if (!((l)->write_locked_map & (1
UL << smp_processor_id())))
\
printk("ASSERT %s:%u %s not writelocked\n", __FILE__, __LINE__, #l); \
printk("ASSERT %s:%u %s not writelocked\n", __FILE__, __LINE__, #l); \
} while(0)
} while(0)
#define MUST_BE_READ_WRITE_UNLOCKED(l) \
#define MUST_BE_READ_WRITE_UNLOCKED(l) \
do { if ((l)->read_locked_map & (1 << smp_processor_id())) \
do { if ((l)->read_locked_map & (1
UL
<< smp_processor_id())) \
printk("ASSERT %s:%u %s readlocked\n", __FILE__, __LINE__, #l); \
printk("ASSERT %s:%u %s readlocked\n", __FILE__, __LINE__, #l); \
else if ((l)->write_locked_map & (1 << smp_processor_id())) \
else if ((l)->write_locked_map & (1
UL
<< smp_processor_id())) \
printk("ASSERT %s:%u %s writelocked\n", __FILE__, __LINE__, #l); \
printk("ASSERT %s:%u %s writelocked\n", __FILE__, __LINE__, #l); \
} while(0)
} while(0)
...
@@ -91,7 +91,7 @@ do { \
...
@@ -91,7 +91,7 @@ do { \
#define READ_UNLOCK(lk) \
#define READ_UNLOCK(lk) \
do { \
do { \
if (!((lk)->read_locked_map & (1 << smp_processor_id()))) \
if (!((lk)->read_locked_map & (1
UL
<< smp_processor_id()))) \
printk("ASSERT: %s:%u %s not readlocked\n", \
printk("ASSERT: %s:%u %s not readlocked\n", \
__FILE__, __LINE__, #lk); \
__FILE__, __LINE__, #lk); \
clear_bit(smp_processor_id(), &(lk)->read_locked_map); \
clear_bit(smp_processor_id(), &(lk)->read_locked_map); \
...
...
net/ipv4/netfilter/ip_conntrack_core.c
View file @
54dd43a6
...
@@ -168,8 +168,8 @@ static inline int expect_cmp(const struct ip_conntrack_expect *i,
...
@@ -168,8 +168,8 @@ static inline int expect_cmp(const struct ip_conntrack_expect *i,
static
void
static
void
destroy_expect
(
struct
ip_conntrack_expect
*
exp
)
destroy_expect
(
struct
ip_conntrack_expect
*
exp
)
{
{
DEBUGP
(
"destroy_expect(%p) use=%d
\n
"
,
exp
,
atomic_read
(
exp
->
use
));
DEBUGP
(
"destroy_expect(%p) use=%d
\n
"
,
exp
,
atomic_read
(
&
exp
->
use
));
IP_NF_ASSERT
(
atomic_read
(
exp
->
use
));
IP_NF_ASSERT
(
atomic_read
(
&
exp
->
use
));
IP_NF_ASSERT
(
!
timer_pending
(
&
exp
->
timeout
));
IP_NF_ASSERT
(
!
timer_pending
(
&
exp
->
timeout
));
kfree
(
exp
);
kfree
(
exp
);
...
@@ -576,7 +576,7 @@ static int early_drop(struct list_head *chain)
...
@@ -576,7 +576,7 @@ static int early_drop(struct list_head *chain)
int
dropped
=
0
;
int
dropped
=
0
;
READ_LOCK
(
&
ip_conntrack_lock
);
READ_LOCK
(
&
ip_conntrack_lock
);
h
=
LIST_FIND
(
chain
,
unreplied
,
struct
ip_conntrack_tuple_hash
*
);
h
=
LIST_FIND
_B
(
chain
,
unreplied
,
struct
ip_conntrack_tuple_hash
*
);
if
(
h
)
if
(
h
)
atomic_inc
(
&
h
->
ctrack
->
ct_general
.
use
);
atomic_inc
(
&
h
->
ctrack
->
ct_general
.
use
);
READ_UNLOCK
(
&
ip_conntrack_lock
);
READ_UNLOCK
(
&
ip_conntrack_lock
);
...
@@ -685,6 +685,14 @@ init_conntrack(const struct ip_conntrack_tuple *tuple,
...
@@ -685,6 +685,14 @@ init_conntrack(const struct ip_conntrack_tuple *tuple,
struct
ip_conntrack_expect
*
,
tuple
);
struct
ip_conntrack_expect
*
,
tuple
);
READ_UNLOCK
(
&
ip_conntrack_expect_tuple_lock
);
READ_UNLOCK
(
&
ip_conntrack_expect_tuple_lock
);
/* If master is not in hash table yet (ie. packet hasn't left
this machine yet), how can other end know about expected?
Hence these are not the droids you are looking for (if
master ct never got confirmed, we'd hold a reference to it
and weird things would happen to future packets). */
if
(
expected
&&
!
is_confirmed
(
expected
->
expectant
))
expected
=
NULL
;
/* Look up the conntrack helper for master connections only */
/* Look up the conntrack helper for master connections only */
if
(
!
expected
)
if
(
!
expected
)
conntrack
->
helper
=
ip_ct_find_helper
(
&
repl_tuple
);
conntrack
->
helper
=
ip_ct_find_helper
(
&
repl_tuple
);
...
@@ -695,12 +703,7 @@ init_conntrack(const struct ip_conntrack_tuple *tuple,
...
@@ -695,12 +703,7 @@ init_conntrack(const struct ip_conntrack_tuple *tuple,
&&
!
del_timer
(
&
expected
->
timeout
))
&&
!
del_timer
(
&
expected
->
timeout
))
expected
=
NULL
;
expected
=
NULL
;
/* If master is not in hash table yet (ie. packet hasn't left
if
(
expected
)
{
this machine yet), how can other end know about expected?
Hence these are not the droids you are looking for (if
master ct never got confirmed, we'd hold a reference to it
and weird things would happen to future packets). */
if
(
expected
&&
is_confirmed
(
expected
->
expectant
))
{
DEBUGP
(
"conntrack: expectation arrives ct=%p exp=%p
\n
"
,
DEBUGP
(
"conntrack: expectation arrives ct=%p exp=%p
\n
"
,
conntrack
,
expected
);
conntrack
,
expected
);
/* Welcome, Mr. Bond. We've been expecting you... */
/* Welcome, Mr. Bond. We've been expecting you... */
...
@@ -1282,9 +1285,9 @@ getorigdst(struct sock *sk, int optval, void *user, int *len)
...
@@ -1282,9 +1285,9 @@ getorigdst(struct sock *sk, int optval, void *user, int *len)
struct
inet_opt
*
inet
=
inet_sk
(
sk
);
struct
inet_opt
*
inet
=
inet_sk
(
sk
);
struct
ip_conntrack_tuple_hash
*
h
;
struct
ip_conntrack_tuple_hash
*
h
;
struct
ip_conntrack_tuple
tuple
=
{
{
inet
->
rcv_saddr
,
struct
ip_conntrack_tuple
tuple
=
{
{
inet
->
rcv_saddr
,
{
inet
->
sport
}
},
{
.
tcp
=
{
inet
->
sport
}
}
},
{
inet
->
daddr
,
{
inet
->
daddr
,
{
inet
->
dport
},
{
.
tcp
=
{
inet
->
dport
}
},
IPPROTO_TCP
}
};
IPPROTO_TCP
}
};
/* We only do TCP at the moment: is there a better way? */
/* We only do TCP at the moment: is there a better way? */
...
...
net/ipv4/netfilter/ip_conntrack_ftp.c
View file @
54dd43a6
...
@@ -367,11 +367,11 @@ static int help(struct sk_buff *skb,
...
@@ -367,11 +367,11 @@ static int help(struct sk_buff *skb,
{
0
}
},
{
0
}
},
{
htonl
((
array
[
0
]
<<
24
)
|
(
array
[
1
]
<<
16
)
{
htonl
((
array
[
0
]
<<
24
)
|
(
array
[
1
]
<<
16
)
|
(
array
[
2
]
<<
8
)
|
array
[
3
]),
|
(
array
[
2
]
<<
8
)
|
array
[
3
]),
{
htons
(
array
[
4
]
<<
8
|
array
[
5
])
},
{
.
tcp
=
{
htons
(
array
[
4
]
<<
8
|
array
[
5
])
}
},
IPPROTO_TCP
}});
IPPROTO_TCP
}});
exp
->
mask
=
((
struct
ip_conntrack_tuple
)
exp
->
mask
=
((
struct
ip_conntrack_tuple
)
{
{
0xFFFFFFFF
,
{
0
}
},
{
{
0xFFFFFFFF
,
{
0
}
},
{
0xFFFFFFFF
,
{
0xFFFF
},
0xFFFF
}});
{
0xFFFFFFFF
,
{
.
tcp
=
{
0xFFFF
}
},
0xFFFF
}});
exp
->
expectfn
=
NULL
;
exp
->
expectfn
=
NULL
;
...
...
net/ipv4/netfilter/ip_conntrack_irc.c
View file @
54dd43a6
...
@@ -192,11 +192,11 @@ static int help(struct sk_buff *skb,
...
@@ -192,11 +192,11 @@ static int help(struct sk_buff *skb,
exp
->
tuple
=
((
struct
ip_conntrack_tuple
)
exp
->
tuple
=
((
struct
ip_conntrack_tuple
)
{
{
0
,
{
0
}
},
{
{
0
,
{
0
}
},
{
htonl
(
dcc_ip
),
{
htons
(
dcc_port
)
},
{
htonl
(
dcc_ip
),
{
.
tcp
=
{
htons
(
dcc_port
)
}
},
IPPROTO_TCP
}});
IPPROTO_TCP
}});
exp
->
mask
=
((
struct
ip_conntrack_tuple
)
exp
->
mask
=
((
struct
ip_conntrack_tuple
)
{
{
0
,
{
0
}
},
{
{
0
,
{
0
}
},
{
0xFFFFFFFF
,
{
0xFFFF
},
0xFFFF
}});
{
0xFFFFFFFF
,
{
.
tcp
=
{
0xFFFF
}
},
0xFFFF
}});
exp
->
expectfn
=
NULL
;
exp
->
expectfn
=
NULL
;
...
...
net/ipv4/netfilter/ip_nat_amanda.c
View file @
54dd43a6
...
@@ -84,7 +84,7 @@ amanda_nat_expected(struct sk_buff **pskb,
...
@@ -84,7 +84,7 @@ amanda_nat_expected(struct sk_buff **pskb,
mr
.
range
[
0
].
flags
|=
IP_NAT_RANGE_PROTO_SPECIFIED
;
mr
.
range
[
0
].
flags
|=
IP_NAT_RANGE_PROTO_SPECIFIED
;
mr
.
range
[
0
].
min
=
mr
.
range
[
0
].
max
mr
.
range
[
0
].
min
=
mr
.
range
[
0
].
max
=
((
union
ip_conntrack_manip_proto
)
=
((
union
ip_conntrack_manip_proto
)
{
htons
(
port
)
});
{
.
udp
=
{
htons
(
port
)
}
});
}
}
return
ip_nat_setup_info
(
ct
,
&
mr
,
hooknum
);
return
ip_nat_setup_info
(
ct
,
&
mr
,
hooknum
);
...
...
net/ipv4/netfilter/ip_nat_core.c
View file @
54dd43a6
...
@@ -763,6 +763,11 @@ do_bindings(struct ip_conntrack *ct,
...
@@ -763,6 +763,11 @@ do_bindings(struct ip_conntrack *ct,
enum
ip_conntrack_dir
dir
=
CTINFO2DIR
(
ctinfo
);
enum
ip_conntrack_dir
dir
=
CTINFO2DIR
(
ctinfo
);
int
proto
=
(
*
pskb
)
->
nh
.
iph
->
protocol
;
int
proto
=
(
*
pskb
)
->
nh
.
iph
->
protocol
;
/* Skip everything and don't call helpers if there are no
* manips for this connection */
if
(
info
->
num_manips
==
0
)
return
NF_ACCEPT
;
/* Need nat lock to protect against modification, but neither
/* Need nat lock to protect against modification, but neither
conntrack (referenced) and helper (deleted with
conntrack (referenced) and helper (deleted with
synchronize_bh()) can vanish. */
synchronize_bh()) can vanish. */
...
@@ -791,6 +796,7 @@ do_bindings(struct ip_conntrack *ct,
...
@@ -791,6 +796,7 @@ do_bindings(struct ip_conntrack *ct,
struct
ip_conntrack_expect
*
exp
=
NULL
;
struct
ip_conntrack_expect
*
exp
=
NULL
;
struct
list_head
*
cur_item
;
struct
list_head
*
cur_item
;
int
ret
=
NF_ACCEPT
;
int
ret
=
NF_ACCEPT
;
int
helper_called
=
0
;
DEBUGP
(
"do_bindings: helper existing for (%p)
\n
"
,
ct
);
DEBUGP
(
"do_bindings: helper existing for (%p)
\n
"
,
ct
);
...
@@ -809,19 +815,21 @@ do_bindings(struct ip_conntrack *ct,
...
@@ -809,19 +815,21 @@ do_bindings(struct ip_conntrack *ct,
continue
;
continue
;
if
(
exp_for_packet
(
exp
,
*
pskb
))
{
if
(
exp_for_packet
(
exp
,
*
pskb
))
{
/* FIXME: May be true multiple times in the
case of UDP!! */
/* FIXME: May be true multiple times in the
DEBUGP
(
"calling nat helper (exp=%p) for packet
\n
"
,
* case of UDP!! */
exp
);
DEBUGP
(
"calling nat helper (exp=%p) for packet
\n
"
,
exp
);
ret
=
helper
->
help
(
ct
,
exp
,
info
,
ctinfo
,
ret
=
helper
->
help
(
ct
,
exp
,
info
,
ctinfo
,
hooknum
,
pskb
);
hooknum
,
pskb
);
if
(
ret
!=
NF_ACCEPT
)
{
if
(
ret
!=
NF_ACCEPT
)
{
READ_UNLOCK
(
&
ip_conntrack_lock
);
READ_UNLOCK
(
&
ip_conntrack_lock
);
return
ret
;
return
ret
;
}
}
helper_called
=
1
;
}
}
}
}
/* Helper might want to manip the packet even when there is no expectation */
/* Helper might want to manip the packet even when there is no
if
(
!
exp
&&
helper
->
flags
&
IP_NAT_HELPER_F_ALWAYS
)
{
* matching expectation for this packet */
if
(
!
helper_called
&&
helper
->
flags
&
IP_NAT_HELPER_F_ALWAYS
)
{
DEBUGP
(
"calling nat helper for packet without expectation
\n
"
);
DEBUGP
(
"calling nat helper for packet without expectation
\n
"
);
ret
=
helper
->
help
(
ct
,
NULL
,
info
,
ctinfo
,
ret
=
helper
->
help
(
ct
,
NULL
,
info
,
ctinfo
,
hooknum
,
pskb
);
hooknum
,
pskb
);
...
...
net/ipv4/netfilter/ip_nat_ftp.c
View file @
54dd43a6
...
@@ -84,7 +84,7 @@ ftp_nat_expected(struct sk_buff **pskb,
...
@@ -84,7 +84,7 @@ ftp_nat_expected(struct sk_buff **pskb,
mr
.
range
[
0
].
flags
|=
IP_NAT_RANGE_PROTO_SPECIFIED
;
mr
.
range
[
0
].
flags
|=
IP_NAT_RANGE_PROTO_SPECIFIED
;
mr
.
range
[
0
].
min
=
mr
.
range
[
0
].
max
mr
.
range
[
0
].
min
=
mr
.
range
[
0
].
max
=
((
union
ip_conntrack_manip_proto
)
=
((
union
ip_conntrack_manip_proto
)
{
htons
(
exp_ftp_info
->
port
)
});
{
.
tcp
=
{
htons
(
exp_ftp_info
->
port
)
}
});
}
}
return
ip_nat_setup_info
(
ct
,
&
mr
,
hooknum
);
return
ip_nat_setup_info
(
ct
,
&
mr
,
hooknum
);
}
}
...
...
net/ipv4/netfilter/ip_nat_snmp_basic.c
View file @
54dd43a6
...
@@ -1308,9 +1308,9 @@ static struct ip_nat_helper snmp = {
...
@@ -1308,9 +1308,9 @@ static struct ip_nat_helper snmp = {
"snmp"
,
"snmp"
,
0
,
0
,
THIS_MODULE
,
THIS_MODULE
,
{
{
0
,
{
__constant_htons
(
SNMP_PORT
)
}
},
{
{
0
,
{
.
udp
=
{
__constant_htons
(
SNMP_PORT
)
}
}
},
{
0
,
{
0
},
IPPROTO_UDP
}
},
{
0
,
{
0
},
IPPROTO_UDP
}
},
{
{
0
,
{
0xFFFF
}
},
{
{
0
,
{
.
udp
=
{
0xFFFF
}
}
},
{
0
,
{
0
},
0xFFFF
}
},
{
0
,
{
0
},
0xFFFF
}
},
nat_help
,
NULL
};
nat_help
,
NULL
};
...
@@ -1319,9 +1319,9 @@ static struct ip_nat_helper snmp_trap = {
...
@@ -1319,9 +1319,9 @@ static struct ip_nat_helper snmp_trap = {
"snmp_trap"
,
"snmp_trap"
,
0
,
0
,
THIS_MODULE
,
THIS_MODULE
,
{
{
0
,
{
__constant_htons
(
SNMP_TRAP_PORT
)
}
},
{
{
0
,
{
.
udp
=
{
__constant_htons
(
SNMP_TRAP_PORT
)
}
}
},
{
0
,
{
0
},
IPPROTO_UDP
}
},
{
0
,
{
0
},
IPPROTO_UDP
}
},
{
{
0
,
{
0xFFFF
}
},
{
{
0
,
{
.
udp
=
{
0xFFFF
}
}
},
{
0
,
{
0
},
0xFFFF
}
},
{
0
,
{
0
},
0xFFFF
}
},
nat_help
,
NULL
};
nat_help
,
NULL
};
...
...
net/ipv6/netfilter/ip6_tables.c
View file @
54dd43a6
...
@@ -101,10 +101,8 @@ struct ip6t_table_info
...
@@ -101,10 +101,8 @@ struct ip6t_table_info
unsigned
int
hook_entry
[
NF_IP6_NUMHOOKS
];
unsigned
int
hook_entry
[
NF_IP6_NUMHOOKS
];
unsigned
int
underflow
[
NF_IP6_NUMHOOKS
];
unsigned
int
underflow
[
NF_IP6_NUMHOOKS
];
char
padding
[
SMP_ALIGN
((
NF_IP6_NUMHOOKS
*
2
+
2
)
*
sizeof
(
unsigned
int
))];
/* ip6t_entry tables: one per CPU */
/* ip6t_entry tables: one per CPU */
char
entries
[
0
];
char
entries
[
0
]
____cacheline_aligned
;
};
};
static
LIST_HEAD
(
ip6t_target
);
static
LIST_HEAD
(
ip6t_target
);
...
@@ -1451,7 +1449,7 @@ int ip6t_register_table(struct ip6t_table *table)
...
@@ -1451,7 +1449,7 @@ int ip6t_register_table(struct ip6t_table *table)
int
ret
;
int
ret
;
struct
ip6t_table_info
*
newinfo
;
struct
ip6t_table_info
*
newinfo
;
static
struct
ip6t_table_info
bootstrap
static
struct
ip6t_table_info
bootstrap
=
{
0
,
0
,
0
,
{
0
},
{
0
},
{
}
,
{
}
};
=
{
0
,
0
,
0
,
{
0
},
{
0
},
{
}
};
newinfo
=
vmalloc
(
sizeof
(
struct
ip6t_table_info
)
newinfo
=
vmalloc
(
sizeof
(
struct
ip6t_table_info
)
+
SMP_ALIGN
(
table
->
table
->
size
)
*
NR_CPUS
);
+
SMP_ALIGN
(
table
->
table
->
size
)
*
NR_CPUS
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment